列表

[!NOTE]

以下内容部分摘录自《Python编程快速上手——让繁琐的工作自动化(第二版)》,并加入了一些自己的理解,仅用于个人交流与学习,如涉及侵权请联系站长删除!

1.列表基础

  • 列表的数据类型:列表是一个值,包含由多个值构成的序列,列表值看起来像这样:['cat', 'bat', 'rat', 'elephant']

  • 用索引取得列表中的单个值

    • 假定列表['cat', 'bat', 'rat', 'elephant']保存在名为spam的变量中 spam[0]将求值为'cat',以此类推。
    • 如果使用的索引超出了列表中值的个数python将给出IndexError错误。索引只能是整数,不能是浮点数 否则将导致TypeError错误。
    • 列表也可以包含其他列表值,如:spam = [['cat', 'bat'], [10, 20, 30, 40, 50]],则:spam[0] = ['cat', 'bat']spam[0][1] = 'bat'spam[1][4] = 50
  • 负数索引:虽然索引从0开始并向上增长,但也可以用负整数作为索引,整数值-1指的是列表中最后一个索引,整数值-2则是指列表中的倒是第二个索引,例如:

    1
    2
    3
    spam = ['cat', 'bat', 'rat', 'elephant']
    print(spam[-1]) # 输出的结果为elephat
    print(spam[-3]) # 输出的结果为bat
  • 利用切片取得子列表

    • 切片可以从列表中于取得多个值,所得的结果是一个新列表。像spam[1:4]这样的形式就是一个切片。

    • 在一个切片中第一个整数是切片开始处的索引,第二个整数是切片结束处的索引,切片向上增长直至第二个引索的值,但不包括它,例如:

      1
      2
      3
      4
      spam = ['cat', 'bat', 'rat', 'elephant']
      print(spam[0:4]) # 输出结果为['cat', 'bat', 'rat', 'elephant']
      print(spam[1:3]) # 输出结果为['bat', 'rat']
      print(spam[0:-1]) # 输出结果为['cat', 'bat', 'rat']
  • len()函数取得列表的长度

    1
    2
    spam = ['cat', 'dog', 'moose']
    print(len(spam)) # 输出结果为3
  • 用索引改变列表中的值

    1
    2
    3
    spam = ['cat', 'bat', 'rat', 'elephant']
    spam[1] = 'aardvark'
    print(spam) # 输出的结果为['cat', 'aardvark', 'rat', 'elephant']
  • 列表连接和列表复制

    • 连接:[1, 2, 3] + ['A', 'B', 'C'] = [1, 2, 3, 'A', 'B', 'C']
    • 复制:['X', 'Y', 'Z'] * 3 = ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'X', 'Y', 'Z']
  • 用del语句从列表删除值

    • del语句将删除列表中索引的值,列表中被删除值后面的所有值,都将向前移动一个索引,例如:

      1
      2
      3
      spam = ['cat', 'bat', 'rat', 'elephant']
      del spam[2]
      print(spam) # 输出的结果为['cat', 'bat', 'elephant']

2.使用列表

在我们第一次开始编程时,可能会为了保存一组很类似的值,而创建多个不同的变量名。而列表的出现,可以让用户不再使用多个类似的变量。例如要输入你自己所有猫的名字:

1
2
3
4
5
6
7
8
9
10
catNames = []
while True:
print('Enter the name of cat ' + str(len(catNames) + 1) + ' (Or enter nothing to stop.):')
name = input()
if name == '':
break
catNames = catNames + [name]
print('The cat names are:')
for name in catNames:
print(name)
  • 解释:首先我们定义了一个名为catNames的空列表用于存储后续输入的猫的名字,之后我们调用while提示用户输入第N只猫的名字,N是当前列表长度加1(如列表中有2个名字,提示输入第3只猫)。接着使用**catNames = catNames + [name]**将用户输入的名字name添加到列表catNames中。最后使用for循环遍历整个列表逐个打印猫的名字。

2.1.列表用于循环

  • 循环可以让一段代码执行一定次数,从技术上讲循环是针对一个序列或列表中的每个值,重复的执行代码块,例如:
1
2
3
4
5
for i in range(4):
print(i)
CYX = [0, 1, 2, 3]
for j in CYX:
print(j)
  • 解释:上面两个print所打印的是相同的是因为range(4)返回的是类似列表的值。

  • 一个常见的python技巧:在for循环中使用range(len(somelist))来迭代列表的每一个索引。例如:

    1
    2
    3
    4
    supplies = ['pen', 'staplers', 'flame-throwers', 'binders']

    for i in range(len(supplies)):
    print('Index ' + str(i) + ' in supplies is: ' + supplies[i])

2.2.innot in操作符

  • inin not操作符可以确定一个值是否在列表中。

  • inin not在表达式中用于连接两个值:1.要在列表中查找的值 2.待查找的列表(这些表达式将求值为bool值)。例如,下面的程序让用户输入一个宠物名字,然后检查该名字是否在宠物列表中:

    1
    2
    3
    4
    5
    6
    7
    myPents = ['Zophie', 'Pooka', 'Fat-tail']
    print('Enter a pet name:')
    name = input()
    if name not in myPents:
    print('I do not have a pet named ' + name)
    else:
    print(name + ' is my pet.')

2.3.多重赋值技巧

  • 多重赋值技巧是一种快捷方式,让你在一行代码中用列表中的值为多个变量赋值。像我们以往的赋值方法就显得有些繁琐,例如:

    1
    2
    3
    4
    5
    6
    cat = ['fat', 'black', 'loud']
    size = cat[0]
    color = cat[1]
    disposition = cat [2]

    print(size, color, disposition)
  • 这样的方法需要我们一次输入变量名和对应的列表索引,很麻烦。不如像这样:

    1
    2
    3
    cat = ['fat', 'black', 'loud']
    size,color,disposition = cat
    print(size, color, disposition)
  • 两串代码输出的结果是一样的,而下面的代码更加简洁,但需要注意的是,变量的数目和列表的长度必须严格相等,否则python将给出ValueError错误。

2.4.enumerate()函数与列表一起使用

  • 如果在for循环中不想用range(len(someList))技术来获取列表中各表项,还可以调用enumerate()函数。

  • 在循环的每次迭代中enumerate()函数将返回两个值:1.表中表项的索引 2.列表中的表项本身。例如,在列表用于循环的代码中使用enumerate()函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    supplies = ['pen', 'staplers', 'flame-throwers', 'binders']

    for index, item in enumerate(supplies):
    print('Index ' + str(index) + ' in supplies is: ' + item)

    # 以下是输出的结果
    # Index 0 in supplies is: pen
    # Index 1 in supplies is: staplers
    # Index 2 in supplies is: flame-throwers
    # Index 3 in supplies is: binders

2.5.random.choice()函数和random.shuffle()函数和列表共同使用

  • random.choice()函数:使用random.choice()函数,将从列表里随即返回一个表项,例如:

    1
    2
    3
    4
    5
    import random
    pets = ['Dogs', 'Cats', 'Moose']
    print(random.choice(pets))

    # 这串代码将输出列表pets中的任意一个表项
  • random.shuffle()函数:使用random.shuffle()函数,将对列表中的表项重新排序,例如:

    1
    2
    3
    4
    5
    people = ['GSY', 'CYX', 'ZZZ', 'XXX']
    random.shuffle(people)
    print(people)

    # 这串代码会将列表people中的表项重新排列并输出

3.增强的赋值操作

  • 在对变量赋值时,常常会用到变量本身,例如:

    1
    2
    3
    4
    5
    6
    7
    spam = 42
    spam = spam + 1
    print(spam)

    abcd = 42
    abcd += 1
    print(abcd)
    • 解释

    • 两串代码运行的结果一样,而+=就是增强操作符,增强操作符还有:

      增强操作符语句 等价操作符语句
      spam += 1 spam = spam + 1
      spam -= 1 spam = spam - 1
      spam *= 1 spam = spam * 1
      spam /= 1 spam = spam / 1
      spam %= 1 spam = spam % 1
    • +=操作符可以完成字符串和列表的连接,*=操作符可以完成字符串和列表的复制。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      CYX = 'Hello,'
      CYX += 'world!'
      print(oppo)

      # 输出的结果为:Hello,world!

      GSY = ['Zophie']
      GSY *= 3
      print(vivo)

      # 输出的结果为:['Zophie', 'Zophie', 'Zophie']

4.方法

  • 方法和函数是一回事,只是它在一个值上进行调用,例如:一个列表值存储在spam变量中,你可以在这个列表上调用index()列表方法,就像spam.index('hello')
  • 方法跟在要调用的值后面,以一个圆点分隔。每种数据类型都有它自己的一组方法,例如:列表数据类型有一些有用的方法,用来查找 、添加、删除或者修改列表中的值。

4.1.用index()方法在列表中查找值

  • 列表值有一个index()方法,可以传入一个值,如果该值存在于列表中,就返回他的索引,如果该值不在列表中,就会报错ValueError,例如:

    1
    2
    3
    spam = ['hello', 'hi', 'howdy', 'heyas']
    print(spam.index('hello')) # 输出的结果为0
    print(spam.index('heyas')) # 输出的结果为3
  • 如果列表中存在重复的值,就返回它第一次出现的引索,例如:

    1
    2
    oppo = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka']
    print(oppo.index('Pooka')) # 输出的结果为1

4.2.用append()方法和insert()方法在列表中添加值

  • append()方法可将参数添加到列表末尾,例如:

    1
    2
    3
    4
    5
    6
    spam = ['cat', 'dog', 'bat']
    spam.append('moose')
    print(spam)

    # 以下是这串代码的输出结果
    # ['cat', 'dog', 'bat', 'moose']
  • insert()方法可以在列表任意索引出插入一个值,例如:

    1
    2
    3
    4
    5
    6
    spam = ['cat', 'dog', 'bat']
    spam.insert(1, 'Chicken')
    print(spam)

    # 以下是这串代码的输出结果
    # ['cat', 'Chicken', 'dog', 'bat', 'moose']
    • 解释insert()方法的第一个参数是新值的索引,第二个参数是要加入的新值。
  • 注意

    1. append()方法与insert()方法都不会将spam的新值作为其返回值 (事实上他们两的返回值都为None),所以直接print(vivo.append('moose'))之类的表述,输出的值会为None
    2. 方法属于单个的数据类型,只能在列表上调用,而不能在字符串或者整型等其它值上调用。

4.3.用remove()方法从列表中删除值

  • remove()方法传入一个值,它将从调用该方法的列表中删除这个值。

    1
    2
    3
    4
    5
    6
    spam = ['cat', 'bat', 'rat', 'elephant']
    spam.remove('bat')
    print(spam)

    # 这串代码输出的结果如下
    # ['cat', 'rat', 'elephant']
    • 注意:如果试图删除列表中不存在的值时,就会发生ValueError错误。
  • 如果该列表中存在出现多次的值,只有第一次会被删除。

    1
    2
    3
    4
    5
    6
    spam =['cat', 'bat', 'rat', 'cat', 'hat', 'cat']
    spam.remove('cat')
    print(spam)

    # 这串代码输出的结果如下
    # ['bat', 'rat', 'cat', 'hat', 'cat']

4.4.用sort()方法将列表中的值排序

  • 包含数值的列表或字符串列表能用sort()方法排序。

    1
    2
    3
    4
    5
    6
    MosHSasA = [2, 5, 3.14, 1, -7]
    MosHSasA.sort()
    print(MosHSasA)

    # 这串代码输出的结果如下
    # [-7, 1, 2, 3.14, 5]
  • 也可以指定reverse关键字参数为True,让sort()方法按照逆序排序。

    1
    2
    3
    4
    5
    6
    MosHSasA = [2, 5, 3.14, 1, -7]
    MosHSasA.sort(reverse=True)
    print(MosHSasA)

    # 这串代码输出的结果如下
    # [5, 3.14, 2, 1, -7]
  • 关于sort()方法应该注意三件事

    1. sort()方法就地的对列表排序不要写出spam = spam.sort()这样的代码。

    2. 不能对既有数字又有字符串值的列表排序,这样会发生TypeError错误。

    3. sort()方法对字符串排序时,使用ASCII码排序,而不是实际的字典排序,这意味着大写字母在小写字母之前,例如:

      1
      2
      3
      4
      5
      6
      spam = ['Alice', 'ants', 'Bob', 'badgers', 'Carol', 'cats']
      spam.sort()
      print(spam)

      # 这串代码输出的结果如下
      # ['Alice', 'Bob', 'Carol', 'ants', 'badgers', 'cats']

      如果要按照普通的字典顺序来排序,就在调用sort()方法时,将关键字参数key设置成str.lower,例如:

      1
      2
      3
      4
      5
      6
      spam = ['a', 'z', 'A', 'Z']
      spam.sort(key=str.lower)
      print(spam)

      # 这串代码输出的结果如下
      # ['a', 'A', 'z', 'Z']

4.5.使用reverse()方法反转列表中的值

  • 如果想要快速反转列表中的项目顺序,可以调用reverse()方法。

    1
    2
    3
    4
    5
    6
    spam = ['cat', 'dog', 'moose']
    spam.reverse()
    print(spam)

    # 这串代码输出的结果如下
    # ['moose', 'dog', 'cat']
  • 注意:和sort()方法一样reverse()方法也不返回列表,这就是为什么要写成spam.reverse()而不是spam = spam.reserve()

5.序列数据类型

  • 列表并不是唯一表示序列值的数据类型,如果将字符串考虑为单个文本字符的列表,那么字符串和列表实际上是相似的。python序列数据类型包括:列表、字符串、由range()返回的范围对象、以及元组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    name = 'Zophie'
    print(name[0])
    print(name[-2])
    print(name[0:4])
    print('Zo' in name)
    print('z' in name)
    print('p' not in name)

    for i in name:
    print('* * * ' + i + ' * * *')

    以上代码的输出结果为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Z
    i
    Zoph
    True
    False
    False
    * * * Z * * *
    * * * o * * *
    * * * p * * *
    * * * h * * *
    * * * i * * *
    * * * e * * *
  • 解释:这表示对于列表的许多操作,也可以作用于字符串和序列数据类型的其他值。

5.1.可变和不可变数据类型

  • 列表和字符串在一个重要方面是不同的:列表是可变的,它可以添加删除或改变;字符串是不可变的,它不能被更改。尝试对一个字符串重新赋值将导致TyperError错误,例如这样:

    1
    2
    name = 'Zophie a cat'
    name[7] = 'the'
  • 改变一个字符串的正确方式是使用:切片和连接来构造一个新的字符串,从旧的字符串里复制一部分。

    1
    2
    3
    4
    name = 'Zophie a cat'
    newName = name[0:7] + 'the' + name[8:12]
    print(name)
    print(newName) # 输出的结果是Zophie the cat
    • 解释name[0:7]将字符串第0个字符到第7个(但不包含第7个)字符保留,name[8:12]类似,之后得到的替换字符串,但注意我们原来的字符串并没有改变。
  • 尽管列表值是可变的,但下面代码中的第二行并没有修改eggs

    1
    2
    3
    eggs = [1, 2, 3]
    eggs = [4, 5, 6]
    print(eggs) # 输出的结果为[4, 5, 6]
    • 解释:这里eggs中的列表值并没有改变,而是整个新的不同的列表值复写了旧的列表值。

    • 如果你确实希望修改eggs中原来的列表,让它包含[4, 5, 6]就要这样做。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      apple = [1, 2, 2]
      del apple[2]
      del apple[1]
      del apple[0]
      apple.append(4)
      apple.append(5)
      apple.append(6)
      print(apple)

      # 输出的结果为[4, 5, 6]
      • 解释:这种情况下eggs最后的列表值与它开始的列表值是一样的,只是这个列表被改变了而不是被复写了。

5.2.元组数据类型

  • 除了两个方面元组数据类型几乎与列表数据类型一样:

    1. 第一个区别在于,元组输入时使用的括号是( )而不是[ ]

      1
      2
      3
      4
      eggs = ('hello', 42, 0.5)
      print(eggs[0])
      print(eggs[1:3])
      print(len(eggs))

      以下是这串代码的输出结果:

      1
      2
      3
      hello
      (42, 0.5)
      3
    2. 另一个区别在于,元组像字符串一样是不可变的,元组不能让他的值被修改、添加或删除。

      1
      2
      apple = ('hello', 42, 0.5)
      apple[1] = 99

      例如,输入上面的代码python会报错TypeError

  • 如果元组只有一个值,可以在括号内该值的后面跟上一个逗号来表明这种情况,否侧python会认为你只是在一个普通的括号内输入了一个值,逗号告诉python这是一个元组,例如,现在用type函数来看看他们各自的类型:

    1
    2
    print(type(('hello',))) # 输出的结果为<class 'tuple'>
    print(type(('hello'))) # 输出的结果为<class 'str'>

5.3.用list()和tuple()函数来转换类型

  • 正如str(42)将返回'42' 即整数42的字符串表示形式一样,函数list()tuple()将传递给它们的值返回成的列表和元组形式。

    1
    2
    3
    print(tuple(['cat', 'dog', 5]))
    print(list(('cat', 'dog', 5)))
    print(list('hello'))

    上面代码的输出结果为:

    1
    2
    3
    ('cat', 'dog', 5)
    ['cat', 'dog', 5]
    ['h', 'e', 'l', 'l', 'o']

6.引用

  • 对于变量保存字符串和整数值这种解释其实是简化了python的实际操作,变量的储存其实是对计算机内存位置的引用,这些位置储存了这些值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    spam = [0, 1, 2, 3, 4, 5]
    cheese = spam
    cheese[1] = 'hello'
    print(spam)
    print(cheese)

    # 这串代码的输出结果如下
    # [0, 'hello', 2, 3, 4, 5]
    # [0, 'hello', 2, 3, 4, 5]
    • 解释:你将一个列表赋给了变量spam,但是在下一行只是将spam变量中的列表引用复制到cheese变量,而不是列表本身。这意味着储存在spamcheese中的值现在指向了同一个列表,列表的本身实际从未复制,所以当你修改cheese变量的第一个元素的同时,也修改了spam变量所指向的同一个列表。

6.1.标识和id()函数

  • 关于刚刚所提到的关于列表的例子或许有一些难以理解,比如像列表这样的行为为什么不会出现在整数或者字符串这些不可变的值上。其实我们可以通过id()函数来解释。

    1
    print(id('Howdy')) # 输出的结果为2269208642992(每次运行代码内存字节都会不同)
    • 解释:当python运行id('Howdy')时它将在计算机的内存中创建'Howdy'字符串,返回存储字符串的数字内存地址,python根据当时计算机上空闲的内存字节来选择此地址,每次运行代码,内存字节都会不同。
  • 像所有字符串一样,'Howdy'是不可变的,无法更改。如果更改变量中的字符串,就会在内存中的其他位置创建新的字符串对象,并且该变量引用这个新的字符串。

    1
    2
    3
    4
    bacon = 'hello'
    print(id(bacon)) # 输出的结果为2269199734896
    bacon += ' world!'
    print(id(bacon)) # 输出的结果为2269208640560
  • 可以修改列表,因为他是可变对象。append()方法不会创建新的列表,他更改现有的列表对象,称之为就地修改对象。

    1
    2
    3
    4
    5
    6
    eggs = ['cat', 'dog']
    print(id(eggs)) # 输出的结果为2342254802752
    eggs.append('moose')
    print(id(eggs)) # 输出的结果为2342254802752
    eggs = ['bat', 'rat', 'cow']
    print(id(eggs)) # 输出的结果为2342254804352
    • 解释:如果两个变量引用同一个列表(如上面所举例的spamcheese变量),并且列表本身发生了变化,那么这两个变量都会受到影响。append()extend()remove()sort()reverse()和其他列表方法会就地改变其列表。

6.2.传递引用

  • 要理解参数如何传递给函数,引用就特别重要。当函数被调用时,参数的值被复制给变元。对于列表、字典, 这意味着变元得到的是引用的复制,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def eggs(someParameter):
    someParameter.append('Hello')

    spam = [1, 2, 3]
    eggs(spam)
    print(spam)

    # 代码的输出结果如下
    # [1, 2, 3, 'Hello']
    • 解释:当eggs()函数被调用时没有使用返回值来为spam变量赋新值,相反他直接就地修改了该列表。尽管spamsomeParameter变量包含了不同的引用,但他们都指向相同的列表。

6.3.copy模块的copy()deepcopy()函数

  • 在处理列表和字典时尽管传递引用是最方便的方法,但是如果函数修改了传入的列表或字典,我们并不希望这些变动影响到原来的字典和列表,此时就可以运用copy()deepcopy()函数。

    1. copy.copy()函数可以用来复制列表或字典这样的可变值,而不只是复制引用。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import copy
      spam = ['A', 'B', 'C', 'D']
      print(id(spam)) # 输出的结果为1773092600256

      cheese= copy.copy(spam)
      print(id(cheese)) # 输出的结果为1773092601216

      cheese[1] = 42
      print(spam) # 输出的结果为['A', 'B', 'C', 'D']
      print(cheese) # 输出的结果为['A', 42, 'C', 'D']
      • 解释:现在cheesespam变量指向独立的列表,这就是为什么将cheese[1]赋值成42时,spam中的列表没有改变。
    2. 如果要复制的列表中包含了列表,就使用copy.deepcopy()函数来代替,这个函数将同时复制它们内部的列表。