好好学Python:从零基础到项目实战
上QQ阅读APP看书,第一时间看更新

3.2 列表

前面已经用了很多次列表,可以看出列表的功能是比较强大的。本节将讨论列表不同于元组和字符串的地方,列表的内容是可变的(mutable),列表有很多比较好用、比较独特的方法,本节将一一进行介绍。

3.2.1 更新列表

序列所拥有的特性,列表都有。在3.1节中所介绍的有关序列的操作,如索引、分片、相加、乘法等操作都适用于列表。本节将介绍一些序列中没有而列表中有的方法,这些方法的作用都是更新列表,如元素赋值、增加元素、删除元素、分片赋值和列表方法等。

1.元素赋值

前面的章节已经大量使用了赋值语句,赋值语句是最简单的改变列表的方式,如a=2就属于一种改变列表的方式。

创建一个列表,列表名为group,group中存放“序列号”上第一组所有5名同学的序号,通过编号标记某个特定位置的元素,对该位置的元素重新赋值,如group[1]=9,就可以实现元素赋值。

拿“序列号”上所有同学的序号来举例,第一组5名同学的序号分别是0、1、2、3、4。由于某种需要,需要序号为1的同学与序号为9的同学交换一下位置及所在的组,对于1和9号同学,各自交换一下座位即可,而导游则将序号为9的同学纳入第一组,序号为1的同学纳入第二组。

这个生活场景读者应该不难理解。而对于计算机来说,比这个生活场景更简单。创建列表group,赋值[0,1,2,3,4]后,计算机就在内存中为group变量开辟了一块内存空间,内存空间中存放数据的形式如图3-1所示。

当执行group[1]=9后,计算机会找到group变量,并找到索引下标为1的内存地址,将内存为1的地址空间的值擦除,再更改上9这个值,就完成了赋值操作,如图3-2所示。

图3-1 内存空间

图3-2 内存空间

在图3-2中可以看到,下标为1对应的数值已经更改为9了,此处为便于读者观察,在下标为1处用了一个波浪线作为特别提示。从此时开始,group列表中的值就变更为0、9、2、3、4了,后续再对group操作,就是在这个列表值的基础上进行操作了。

用代码实现上述操作如下:

>>> group=[0,1,2,3,4]
>>> group[1]=9         #索引下标为1的元素重新赋值为9
>>> group
[0, 9, 2, 3, 4]
>>> group[3]=30   #同理,可以将索引下标为3的元素重新赋值为30
>>> group
[0, 9, 2, 30, 4]

这里不要忘记索引下标的编号是从0开始的。

由输出结果可以看到,可以根据索引下标编号对列表中某个元素重新赋值。

既然可以重新赋值,是否可以对列表中的元素赋不同类型的值呢?对上面得到的group列表,在交互模式下做如下尝试:

>>> group[2]='xiaomeng'    #对编号为2的元素赋值,赋一个字符串
>>> group
[0, 9, 'xiaomeng', 30, 4]
>>> type(group)
<class 'list'>
>>> type(group[1])         #别忘了查看类型函数的使用
<class 'int'>
>>> type(group[2])
<class 'str'>

由输出结果可以看到,可以对一个列表中的元素赋以不同类型的值。在上面的示例中,列表group中既有int类型的值,也有str类型的值。

假如对列表赋值时,使用的索引下标编号超过了列表中的最大索引下标编号,是否可以赋值成功?得到结果会是怎样的?继续对group列表操作,group列表中当前有5个元素,最大索引下标是4,即group[4],这里尝试对group[5]赋值,在交互模式下输入:

在上面的示例中,group列表的最大索引下标编号是4,当给索引下标编号为5的元素赋值时出错,错误提示的信息是:列表索引超出范围。

提示

不能为一个不存在元素的位置赋值,若强行赋值,程序会报错。

2.增加元素

由上面元素赋值的示例可以看到,不能为一个不存在的元素位置赋值,列表一旦创建,就不能再向这个列表中增加元素了。

不能向列表中增加元素这种情况可能会让我们比较难堪,毕竟在实际项目应用中,一个列表到底要创建为多大,经常是不能预先知道的。

那么,这种问题该怎么处理呢?列表增加元素的操作在实际应用中会有比较多的应用场景,也是一个高频次的操作,Python中是否提供了对应的方法帮助我们做这件事情呢?

答案是肯定的。接着上面的示例,下面尝试将字符串try添加到group列表中。在交互模式下输入:

>>> group
[0, 9, 'xiaomeng', 30, 4]
>>> group.append('try')
>>> group
[0, 9, 'xiaomeng', 30, 4, 'try']

由示例输出结果看到,在Python中提供了一个append()方法,该方法可以帮助我们解决前面遇到的困惑。

append()方法是一个用于在列表末尾添加新对象的方法。append()方法的语法格式如下:

list.append(obj)

此语法中,list代表列表,obj代表需要添加到list列表末尾的对象。

提示

append()方法的使用方式是list.append(obj),list要为已经创建的列表,obj不能为空。

对于append()方法的使用,需要补充说明:append()方法操作列表时,返回的列表不是一个新列表,而是直接在原来的列表上做修改,然后将修改过的列表直接返回。如果是创建新列表,就会多出一倍的存储空间。以group列表为例,未使用append()方法之前,group列表中的内容是[0, 9,'xiaomeng', 30, 4],这是已经占用了一块存储空间的值。使用append()方法后,若创建了新列表,就会在内存中再开辟一块新的存储空间,新开辟的存储空间中存放的内容是[0, 9, 'xiaomeng', 30, 4, 'try'],和原列表比起来,就相当于增加了一倍的存储空间。而直接修改列表的情形会是这样的:内容是[0, 9,'xiaomeng', 30, 4]的存储空间继续占有,使用append()方法后,会在现有的存储空间中增加一小块内存,用来存放新增加的try字符串,相对于原列表,仅仅增加了try字符串所占据的存储,而不是增加一倍的存储空间。

使用append()方法,可以向列表中增加各种类型的值。

继续操作group列表,append()使用的示例如下:

>>> group
[0, 9, 'xiaomeng', 30, 4, 'try']
>>> group.append('test')             #向列表添加字符串
>>> group
[0, 9, 'xiaomeng', 30, 4, 'try', 'test']
>>> group.append(3)                  #向列表添加数字
>>> group
[0, 9, 'xiaomeng', 30, 4, 'try', 'test',3]

3.删除元素

由上面的示例输出接口可以得知:可以向数字序列中添加字符串,也可以向字符串序列中添加数字。

前面学习了向列表中增加元素,可以使用append()方法来实现。列表中既然可以增加元素,那是否可以删除元素呢?

继续操作group列表,示例如下:

>>> group
[0, 9, 'xiaomeng', 30, 4, 'try', 'test']
>>> len(group)         #使用序列中获取长度的函数
7
>>> del group[6]       #删除最后一个元素,注意索引下标与序列长度的关系
>>> print('删除最后一个元素后的结果:',group)
删除最后一个元素后的结果: [0, 9, 'xiaomeng', 30, 4, 'try']
>>> len(group)
6
>>> group
[0, 9, 'xiaomeng', 30, 4, 'try']
>>> del group[2]   #删除索引下标为2的元素
>>> print('删除索引下标为2的元素后的结果:',group)
删除索引下标为2的元素后的结果: [0, 9, 30, 4, 'try']
>>> len(group)
5

由输出结果可以看到,使用del可以删除列表中的元素。

上面的示例中使用del删除了group列表中的第7个元素,删除元素后,原来有7个元素的列表会变成有6个元素的列表。

使用del除了可以删除列表中的字符串,也可以删除列表中的数字。

继续操作group列表,在交互模式下输入:

>>> group
[0, 9, 30, 4, 'try']
>>> len(group)
5
>>> del group[3]
>>> print('删除索引下标为3的元素后的结果:',group)
删除索引下标为3的元素后的结果: [0, 9, 30, 'try']
>>> len(group)
4

由输出结果可以看到,已经从group列表中删除了对应的数字。

除了删除列表中的元素,del还能用于删除其他元素,具体将在后续章节做详细介绍。

4.分片赋值

分片赋值是列表一个强大的特性。已经在3.1节讲解过分片的定义与实现。

在继续往下之前需要补充一点:如前面所说,通过a=list()的方式可以初始化一个空的列表。但若写成如下形式:

list(str)或a=list(str)

则list()方法会将字符串str转换为对应的列表,str中的每个字符将被转换为一个列表元素,包括空格字符。list()方法可以直接将字符串转换为列表。该方法的一个功能就是根据字符串创建列表,有时这么操作会很方便。list()方法不仅适用于字符串,所有类型的序列它都适用。

    >>> list('北京将举办2020年的冬奥会')
    ['北', '京', '将', '举', '办', '2', '0', '2', '0', '年', '的', '冬', '奥', '会']
    >>> greeting=list('welcome to beijing')
    >>> greeting
    ['w', 'e', 'l', 'c', 'o', 'm', 'e', ' ', 't', 'o', ' ', 'b', 'e', 'i', 'j', 'i',
'n', 'g']
    >>> greeting[11:18]
    ['b', 'e', 'i', 'j', 'i', 'n', 'g']
    >>> greeting[11:18]=list('china')
    >>> greeting
    ['w', 'e', 'l', 'c', 'o', 'm', 'e', ' ', 't', 'o', ' ', 'c', 'h', 'i', 'n', 'a']

由输出结果可以看到,可以直接使用list()将字符串变换为列表,也可以通过分片赋值直接对列表进行变更。

示例中我们首先将字符串“北京将举办2020年的冬奥会”使用list()方法转变为列表,接着将字符串“welcome to beijing”也使用list()方法转变为列表,并将结果赋值给greeting列表。最后通过分片操作变更greeting列表中索引下标编号为11到18之间的元素,即将beijing替换为china。

除了上面展示的功能,分片赋值还有什么强大的功能呢?先看下面的示例:

>>> greeting = list('hi')
>>> greeting
['h', 'i']
>>> greeting[1:] = list('ello')
>>> greeting
['h', 'e', 'l', 'l', 'o']

分析如下:首先给greeting列表赋值['h', 'i'],后面通过列表的分片赋值操作将编号1之后的元素变更,即将编号1位置的元素替换为e,但是编号2之后没有元素,怎么能操作成功呢?并且一直操作到编号为4的位置呢?

这就是列表的分片赋值的另一个强大的功能:可以使用与原列表不等长的列表将分片进行替换。

除了分片替换,列表的分片赋值还有哪些新功能呢?接着看下面的示例:

>>> field = list('ae')
>>> field
['a', 'e']
>>> field[1: 1] = list('bcd')
>>> field
['a', 'b', 'c', 'd', 'e']
>>> goodnews = list('北京将举办冬奥会')
>>> goodnews
['北', '京', '将', '举', '办', '冬', '奥', '会']
>>> goodnews[5: 5] = list('2022年的')
>>> goodnews
['北', '京', '将', '举', '办', '2', '0', '2', '2', '年', '的', '冬', '奥', '会']

由输出结果可以看到,使用列表的分片赋值功能,可以在不替换任何原有元素的情况下在任意位置插入新元素。读者可自行尝试在上面示例的其他位置进行操作。

当然,上面的示例程序的实质是“替换”了一个空分片,实际发生的操作是在列表中插入了一个列表。

该示例的使用是否让你想起了前面使用过的append()方法,不过分片赋值比append()方法的功能强大很多,append()方法只能在列表尾部增加元素,不能指定元素的插入位置,并且一次只能插入一个元素;而分片赋值可以在任意位置增加元素,并且支持一次插入多个元素。

看到这里,是否同时想起了前面删除元素的操作,分片赋值是否支持类似删除的功能呢?

分片赋值中也提供了类似删除的功能。示例如下:

>>> field = list('abcde')
>>> field
['a', 'b', 'c', 'd', 'e']
>>> field[1: 4] = []
>>> field
['a', 'e']
>>> field = list('abcde')
>>> del field[1: 4]
>>> field
['a', 'e']
>>> goodnews = list('北京将举办2022年的冬奥会')
>>> goodnews
['北', '京', '将', '举', '办', '2', '0', '2', '2', '年', '的', '冬', '奥', '会']
>>> goodnews[5: 11] = []
>>> goodnews
['北', '京', '将', '举', '办', '冬', '奥', '会']

从上面的输出结果可以看到,通过分片赋值的方式,将想要删除的元素赋值为空列表,可以达到删除对应元素的效果。并且列表中的分片删除和分片赋值一样,可以对列表中任意位置的元素进行删除。

3.2.2 嵌套列表

目前,我们接触到的列表都是一维的,也就是一个列表里面有多个元素,每个元素对应一个数值或一个字符串。那么,列表中是否可以有列表呢,这里就引入了多维列表的概念。

所谓多维列表,就是列表中的元素也是列表。就如“序列号”大巴上的同学,目前分成了6个小组,对于“序列号”大巴,我们可以看成是一个列表,6个小组也可以看成在“序列号”大巴里的6个列表,每个列表中又分别存放了各个同学的序号。

在交互模式下表示如下:

    >>> bus=[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19],[20,21,
22,23,24],[25,26, 27,28,29,30]]
    >>> bus
    [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19],
[20, 21, 22, 23, 24], [25, 26, 27, 28, 29, 30]]
    >>> group1=bus[0]
    >>> group1  #取得第一组所有同学的序号
    [0, 1, 2, 3, 4]
    >>> type(group1)
    <class 'list'>
    >>> group2=bus[1]  #取得第二组所有同学的序号
    >>> group2
    [5, 6, 7, 8, 9]
    >>> type(group2)
    <class 'list'>
    >>> group6=bus[5]  #取得第三组所有同学的序号
    >>> group6
    [25, 26, 27, 28, 29, 30]
    >>> type(group6)
    <class 'list'>
    >>> number0=group1[0]   #取得0号同学的序号
    >>> number0
    0
    >>> number30=group6[5]  #取得30号同学的序号
    >>> number30
    30

由操作结果得知,在列表中可以嵌套列表,嵌套的列表取出后还是列表。多维列表的操作和一维列表差不多,只不过操作多维列表时,需要先逐步得到多维列表中的一维列表元素,拿到一维列表元素后,其操作方式就如同一维列表了。当然,也可以对多维列表做分片操作,本书不做具体示例演示。

3.2.3 列表方法

方法是与对象有紧密联系的函数,对象可能是列表、数字,也可能是字符串或其他类型的对象。方法的调用语法格式如下:

对象.方法(参数)

比如前面用到的append()方法就是这种形式的,由列表方法的语法和前面append()方法的示例可知,方法的调用方式是将对象放到方法名之前,两者之间用一个点号隔开,方法后面的括号中可以根据需要带上参数。除了语法上有一些不同,方法调用和函数调用很相似。

列表中有append()、extend()、index()、sort()等常用的方法,下面逐一进行介绍。

1.append()

append()方法的语法格式如下:

list.append(obj)

此语法中,list代表列表,obj代表待添加的对象。

append()方法在前面已经介绍过,该方法的功能是在列表的末尾添加新对象。

在实际项目应用中,列表中的append()方法是使用频率最高的一个方法,涉及列表操作的,都或多或少需要用到append()方法进行元素的添加。

2.extend()

extend()方法的语法格式如下:

list.extend(seq)

此语法中,list代表被扩展列表,seq代表需要追加到list中的元素列表。

extend()方法用于在列表末尾一次性追加另一个列表中的多个值(用新列表扩展原来的列表),也就是列表的扩展。

在extend()方法的使用过程中,list列表会被更改,但不会生成新的列表。

使用该方法的示例如下:

>>> a=['hello','world']
>>> b=['python','is','funny']
>>> a.extend(b)
>>> a
['hello', 'world', 'python', 'is', 'funny']

由操作结果可知,extend()方法很像序列连接的操作。

extend()方法和序列相加有什么不同之处?先看看下面的示例:

>>> a=['hello','world']
>>> b=['python','is','funny']
>>> a+b
['hello', 'world', 'python', 'is', 'funny']
>>> a
['hello', 'world']
>>> a.extend(b)
>>> a
['hello', 'world', 'python', 'is', 'funny']

由输出结果可以看到,使用序列相加和使用extend()得到的结果是相同的,但使用序列相加时,并不改变任何变量的值,而使用extend()方法时,会更改变量的值。

由此我们得出,extend()方法和序列相加的主要区别是:extend()方法修改了被扩展的列表,如这里的a,执行a.extend(b)后,a的值变更了;原始的连接操作会返回一个全新的列表,如上面的示例中,a与b的连接操作返回的是一个包含a和b副本的新列表,而不会修改原始的变量。

上述示例也可以使用前面学习的分片赋值来实现,在交互模式下输入:

>>> a=['hello','world']
>>> b=['python','is','funny']
>>> a[len(a):]=b
>>> a
['hello', 'world', 'python', 'is', 'funny']

可以看到,最终得到的结果和使用extend()方法一样,不过看起来没有extend()方法易懂,这里只是作为一个演示示例,在实际项目开发过程中,不建议使用该方式。

在实际项目应用中,extend()方法也是一个使用频率较高的方法,特别是在涉及多个列表的合并时,使用extend()方法非常便捷。

3.index()

index()方法的语法格式如下:

list.index(obj)

此语法中,list代表列表,obj代表待查找的对象。

index()方法用于从列表中搜索某个值,返回列表中找到的第一个与给定参数匹配的元素的索引下标位置。

使用该方法的示例如下:

由输出结果可以看到,当使用index()方法搜索字符串hello时,index()方法返回字符串hello在列表中的索引位置0;使用index()方法搜索字符串python时,index()方法返回字符串python在序列中的索引位置2,索引得到的位置跟元素在序列中的索引下标编号一样。如果搜索的是列表中不存在的字符串,就会出错,所以对于不在列表中的元素,用index()方法操作会报错。

在实际项目应用中,index()方法的使用不是很多,在功能上,使用in可以达到和index()相同的功能,除非要返回搜索对象的具体索引位置时,才考虑使用index()方法,其他情形使用in会更高效和便捷。

4.insert()

insert()方法的语法格式如下:

list.insert(index,obj)

此语法中,list代表列表,index代表对象obj需要插入的索引位置,obj代表要插入列表中的对象。

insert()方法用于将对象插入列表。

使用该方法的示例如下:

>>> num=[1,2,3]
>>> print('插入之前的num:',num)
插入之前的num: [1, 2, 3]
>>> num.insert(2,'插入位置在2之后,3之前')
>>> print('插入之后的num:',num)
插入之后的num: [1, 2, '插入位置在2之后,3之前', 3]

由上面的操作过程及输出结果可以看到,insert()方法操作列表是非常方便的。

与extend()方法一样,insert()方法的操作也可以使用分片赋值实现。

>>> num=[1,2,3]
>>> print('插入之前的num:',num)
插入之前的num: [1, 2, 3]
>>> num[2:2]=['插入位置在2之后,3之前']
>>> print('插入之后的num:',num)
插入之后的num: [1, 2, '插入位置在2之后,3之前', 3]

输出结果和insert()操作的结果一样,但看起来没有使用insert()容易理解。

在实际项目应用中,insert()方法较多地用于在列表指定位置插入指定元素,多用于单个元素的插入,当涉及大量元素插入时,使用分片赋值要更好一些。

5.sort()

sort()方法的语法格式如下:

list.sort(func)

此语法中,list代表列表,func为可选参数。如果指定该参数,就会使用该参数的方法进行排序。

sort()方法用于对原列表进行排序,如果指定参数,就使用参数指定的比较方法进行排序。

使用该方法的示例如下:

>>> num=[5,8,1,3,6]
>>> num.sort()
>>> print('num调用sort方法后:',num)
num调用sort方法后: [1, 3, 5, 6, 8]

由上面输出的结果可知,sort()方法改变了原来的列表,即sort()方法是直接在原来列表上做修改的,而不是返回一个已排序的新的列表。

我们前面学习过几个改变列表却不返回值的方法(如append()方法),不能将操作结果赋给一个变量,这样的行为方式在有一些情况下是非常合常理并且很有用的。如果用户只是需要一个排好序的列表的副本,同时又需要保留列表原本结构不变时,使用不修改列表结构的方法就很有必要了。操作示例如下:

>>> num=[5,8,1,3,6]
>>> n=num.sort()
>>> print('变量n的结果是:',n)
变量n的结果是: None
>>> print('列表num排序后的结果是:',num)
列表num排序后的结果是: [1, 3, 5, 6, 8]

由输出结果可以看到,输出结果和我们预期的不一样。这是因为sort()方法修改了列表num,但sort()方法是没有返回值的,或返回的就是一个None,所以我们最后看到的是已排序的num和sort()方法返回的None。

要想实现结构不改变的num变量,又要使num变量值排序,正确的实现方式是先把num的值赋给变量n,然后使用sort()方法对n进行排序,在交互模式下输入:

>>> num=[5,8,1,3,6]
>>> n=num                            #将列表num赋值给n
>>> n.sort()
>>> print('变量n的结果是:',n)
变量n的结果是: [1, 3, 5, 6, 8]
>>> print('num的结果是:',num)        #num也被排序了
num的结果是: [1, 3, 5, 6, 8]
>>> num=[5,8,1,3,6]
>>> n=num[:]                    #将列表num切片后赋值给n
>>> n.sort()
>>> print('变量n的结果是:',n)
变量n的结果是: [1, 3, 5, 6, 8]
>>> print('num的结果是:',num)        #num保持原样
num的结果是: [5, 8, 1, 3, 6]

由上面的执行结果可以看到,通过直接将变量num的值赋给变量n,对n使用sort()方法排序后,原来的num变量也被排序了,这是为什么呢?

这是因为在内存的分配中,num变量创建时,计算机为num变量分配了一块内存,用于存放num指向的变量值,当执行n=num时,计算机并没有再开辟一块新的内存用于存放变量n所指向的变量值,而是将n指向了与num相同的一块内存地址,也就是这时变量num和n都是指向同一块内存地址。所以当使用sort()方法对n做排序后,内存中的值发生变化,所以最后打印出来的num变量值也是排序后的变量值。

同时也可以看到,将num分片后的值赋给变量n后,对n进行排序的结果不影响num的变量值。这是因为对num分片后赋值给变量n时,变量n开辟的是一块新的内存空间,也就是变量n指向的内存与变量num不是同一块了,所有对变量n的操作不会影响变量num。

在项目实战时,要注意该类问题的处理,若不想更改原列表的数据结构,又想对变量值做排序,可以对原列表分片后赋给一个新变量,对新变量进行排序即可。

在Python中,sort()方法有一个有同样功能的函数—sorted函数。该函数可以直接获取列表的副本进行排序,sorted函数的使用方式如下:

>>> num=[5,8,1,3,6]
>>> n=sorted(num)
>>> print('变量n的操作结果是:',n)
变量n的操作结果是: [1, 3, 5, 6, 8]
>>> print('num的结果是:',num)   #num保持原样
num的结果是: [5, 8, 1, 3, 6]

执行结果和前面操作的一样。sorted函数可以用于任何序列,返回结果都是一个列表。例如下面的操作:

>>> sorted('python')
['h', 'n', 'o', 'p', 't', 'y']
>>> sorted('321')
['1', '2', '3']

在实际项目应用中,sort()方法应用频率不是很高,在需要涉及一些稍微简单的排序时会使用sort()方法,很多时候可能需要开发者自己实现有针对性的排序方法。

6.copy()

copy()方法的语法格式如下:

list.copy()

此语法中,list代表列表,不需要传入参数。

copy()方法用于复制列表,类似于a[:]。使用该方法的示例如下:

>>> field=['study','python','is','happy']
>>> copyfield=field.copy()
>>> print('复制操作结果:',copyfield)
复制操作结果: ['study', 'python', 'is', 'happy']

操作结果和该方法的意思一样,是原原本本的复制操作。

对于前面遇到的sort()方法中的困惑,可以通过copy()方法来解决,具体实现如下:

>>> num=[5,8,1,3,6]
>>> num
[5, 8, 1, 3, 6]
>>> n=num.copy()
>>> n
[5, 8, 1, 3, 6]
>>> n.sort()
>>> n
[1, 3, 5, 6, 8]
>>> num
[5, 8, 1, 3, 6]

由输出结果可以看到,调用copy()方法后,对n进行排序,并不影响num变量的值。

在实际项目应用中,copy()方法的使用频率不是很高,但copy()方法是一个比较有用的方法,在列表的结构复制上很好用,效率也比较高。

由上面的示例看到,调用pop方法移除元素时,在交互模式下会告知我们移除了哪个元素,如上面示例中的funny、is。移除funny时未传参数,默认移除最后一个;is的移除则是根据传入的编号3进行的。

使用pop方法可以实现一种常见的数据结构——栈。

栈的原理就像堆放盘子一样,一次操作一个盘子,要将若干盘子堆成一堆,只能在一个盘子的上面放另一个盘子;要拿盘子时,只能从顶部一个一个往下拿,最后放入的盘子是最先被拿的。栈也是这样,最后放入栈的最先被移除,称为LIFO(Last In First Out),即后进先出。

栈中的放入和移除操作有统一的称谓——入栈(push)和出栈(pop)。Python没有入栈方法,但可以使用append方法代替。pop方法和append方法的操作结果恰好相反,如果入栈(或追加)刚刚出栈的值,最后得到的结果就不会变,例如以下操作:

>>> num = [1, 2, 3]
>>> num.append(num.pop())            #追加默认出栈的值
>>> print('num追加默认出栈值的操作结果:',num)
num追加默认出栈值的操作结果: [1, 2, 3]

由上面的操作结果看到,通过追加默认出栈的值得到的列表和原来是一样的。

7.remove()

remove()方法的语法格式如下:

list.remove(obj)

此语法中,list代表列表,obj为列表中要移除的对象。

remove()方法用于移除列表中某个值的第一个匹配项。使用该方法的示例如下:

由输出结果可以看到,remove()只移除列表中找到的第一个匹配值,找到的第二个之后的匹配值不会被移除。

通过查看上面定义的列表,通过计数,可以知道列表中有3个“精神”,调用移除方法remove()后,删除了第一个,后面两个仍然在列表中。

同时,不能移除列表中不存在的值,系统会告知移除的对象不在列表中。

此处需要补充一点:remove()方法没有返回值,是一个直接对元素所在位置变更的方法,它修改了列表却没有返回值。

在实际项目应用中,remove()方法的使用频率不高。

8.pop()

pop()方法的语法格式如下:

list.pop(obj=list[-1])

此语法中,list代表列表,obj为可选择的参数,代表要移除列表元素的对象。

pop()方法用于移除列表中的一个元素,并且返回该元素的值。

在使用pop()方法时,若没有指定需要移除的元素,则默认移除列表中的最后一个元素。pop()方法的使用示例如下:

>>> field=['hello', 'world', 'python', 'is', 'funny']
>>> field.pop()  #不传参数,默认移除最后一个元素
'funny'
>>> print('移除元素后的field:',field)
移除元素后的field: ['hello', 'world', 'python', 'is']
>>> field.pop(3)  #移除编号为3的元素
'is'
>>> print('移除元素后的field:',field)
移除元素后的field: ['hello', 'world', 'python']
>>> field.pop(0)
'hello'
>>> print('移除元素后的field:',field)
移除元素后的field: ['world', 'python']

由输出结果可以看到,调用pop()方法移除元素时,在交互模式下会告知我们此次操作移除了哪个元素,如上面示例中的funny、is。在对field变量使用pop()方法的过程中,有一处没有指定移除哪个元素,结果默认移除了funny这个元素,即列表的最后一个元素;is的移除则是根据传入的索引下标编号3进行的。

提示

在Python中,pop()方法是唯一一个既能修改列表又能返回元素值(除了None)的列表方法。

使用pop()方法可以实现一种常见的数据结构——栈。

栈的原理就像堆放盘子一样,一次操作一个盘子,要将若干盘子堆成一堆,只能在一个盘子的上面放另一个盘子;要拿盘子时,只能从顶部一个一个地往下拿,最后放入的盘子是最先被拿的。栈也是这样,最后放入栈的元素最先被移除,称为LIFO(Last In First Out),即后进先出。

栈中的放入和移除操作有统一的称谓——入栈(push)和出栈(pop)。Python没有入栈方法,但可以使用append()方法代替。pop()方法和append()方法的操作结果恰好相反,如果入栈(或追加)刚刚出栈的值,最后得到的结果就不会变,例如:

>>> num=[1,2,3]
>>> num.append(num.pop())            #追加默认出栈的值
>>> print('num追加默认出栈值的操作结果:',num)
num追加默认出栈值的操作结果: [1, 2, 3]

由操作结果可以看到,通过追加默认出栈的值得到的列表和原来的是一样的。

在实际项目应用中,pop()方法的使用频率并不高,但不能以此否认pop()方法的使用价值,pop()是一个非常有使用价值的方法,在一些问题的处理上它有独特的功能特性,读者在使用时可以多加留意。

9.reverse()

reverse()方法的语法格式如下:

list.reverse()

此语法中,list代表列表,该方法不需要传入参数。

reverse()方法用于反向列表中的元素。使用该方法的示例如下:

>>> num=[1,2,3]
>>> print('列表反转前num:',num)
列表反转前num: [1, 2, 3]
>>> num.reverse()
>>> print('列表反转后:',num)
列表反转后: [3, 2, 1]

由输出结果可以看到,该方法改变了列表但不返回值(和前面的remove()方法一样)。

提示

如果需要对一个序列进行反向迭代,那么可以使用reversed函数。这个函数并不返回列表,而是返回一个迭代器(Iterator)对象(该对象在后面会详细介绍),可以通过list函数把返回的对象转换为列表,例如:

>>> num = [1, 2, 3]

>>> print('使用reversed函数翻转结果:', list(reversed(num)))

使用reversed函数翻转结果:[3, 2, 1]

输出结果对原序列反向迭代了。

在实际项目应用中,reverse()方法一般会配合sort()方法一起使用,目的是更方便于排序,为排序节省时间或内存开销,对于不同业务场景会有不同的节省方式。

10.clear()

clear()方法的语法格式如下:

list.clear()

此语法中,list代表列表,不需要传入参数。

clear()方法用于清空列表,类似于del a[:]。使用该方法的示例如下:

>>> field=['study','python','is','happy']
>>> field.clear()
>>> print('field调用clear方法后的结果:',field)
field调用clear方法后的结果: []

由操作结果看到,clear()方法会清空整个列表,调用该方法进行清空操作很简单,但也要小心,因为一不小心就可能把整个列表都清空了。

在实际项目应用中,clear()方法一般应用在涉及大量列表操作,且类别元素比较多的场景中,在列表元素比较多时,一般会涉及分批次的操作,每批次操作时,为减少对内存的占用,一般会使用clear()方法先清空列表,高效且快速。

11.count()

count()方法的语法格式如下:

list.count(obj)

此语法中,list代表列表,obj代表列表中统计的对象。

count()方法用于统计某个元素在列表中出现的次数。使用该方法的示例如下:

>>> field=list('hello,world')
>>> field
['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd']
>>> print('列表field中,字母o的个数:',field.count('o'))   #统计列表中的字符个数
列表field中,字母o的个数: 2
>>> print('列表field中,字母l的个数:',field.count('l'))
列表field中,字母l的个数: 3
>>> print('列表field中,字母a的个数:',field.count('a'))
列表field中,字母a的个数: 0
>>> listobj=[123, 'hello', 'world', 123]
>>> listobj=[26, 'hello', 'world', 26]
>>> print('数字26 的个数:',listobj.count(26))
数字26 的个数: 2
>>> print('hello的个数:',listobj.count('hello'))#统计字符串个数
hello的个数: 1
>>> ['a','c','a','f','a'].count('a')
3
>>> mix=[[1,3],5,6,[1,3],2,]
>>> print('嵌套列表mix中列表[1,3]的个数为:',mix.count([1,3]))
嵌套列表mix中列表[1,3]的个数为:2

在实际项目应用中,count()方法用得比较少,是一个低频使用的方法。

12.高级排序

如果希望元素按特定方式进行排序(不是sort方法默认的按升序排列元素),就可以自定义比较方法。

sort方法有两个可选参数,即key和reverse。要使用它们,就要通过名字指定,我们称之为关键字参数。例如下面的示例:

>>> field = ['study', 'python', 'is', 'happy']
>>> field.sort(key=len)                        #按字符串由短到长排序
>>> field
>>> field.sort(key=len, reverse=True)               #按字符串由长到短排序,传递两个参数
>>> field
['python', 'study', 'happy', 'is']
['is', 'study', 'happy', 'python']
>>> num = [5, 8, 1, 3, 6]
>>> num.sort(reverse=True)                     #排序后逆序
>>> num
[8, 6, 5, 3, 1]

由上面的操作结果可知,sort方法带上参数后的操作是很灵活的,可以根据自己的需要灵活使用该方法。关于自定义函数,后续章节会有更详细的介绍。

在实际项目应用中,高级排序应用的场景比较多,也各有特色,不同的项目会有不同的需求场景,需要视具体项目而定。