![Python程序设计](https://wfqqreader-1252317822.image.myqcloud.com/cover/842/25449842/b_25449842.jpg)
2.3 列表
2.3.1 列表基本特征
前面已经介绍完数字类型,其通常只处理单个元素对象,而在实际开发过程中经常会将一系列对象并列放在一起形成一个集合来进行操作,或者形成数据结构,这就是接下来要讲的序列。
序列描述了数据类型的一种形态,具体的数据类型有很多,包括列表、元组、字符串,这些都属于序列类型。
从特性上来讲它可以分为两类,一类是可变序列,另一类是不可变序列。
可变序列:序列中的某个元素支持在原位置被改变。
不可变序列:不允许在原位置改变某个元素或对象的值。
首先来看可变序列中使用频率最高的列表。列表可以说是Python语言中使用频率最高的一个有序序列(这里的有序指的是先后顺序而不是大小顺序)。
1.列表的定义
列表(list):可以包含任意对象的有序集合(可以包含类型统一的整数,也可以包含不同类型的如串、元组、字典、自己定义的类等,并且它是有序的,顺序是可以自定义的)。
2.列表的声明
来看处理学生成绩的例子,当处理一个学生一门课程的成绩时,可以通过定义一个变量来保存成绩。如果要处理很多门课程的成绩,当然也可以定义多个变量来保存成绩,但它们是一个有机的整体或在逻辑上有一定的关系,此时可以把它们组织到一个数据结构里,这就是列表。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/71_1.jpg?sign=1739333740-1aq5sPnyiEQFL1qPpKELtzAih0o0SPa0-0-e993b086807b2b9180a7cf05fb51dfe5)
列表的声明:列表用一对中括号来声明,中括号里写上多个元素,中间以逗号隔开,逗号是默认的分隔符,这些元素可以是任意类型的数据。
3.列表的特性
列表具有以下特性:
(1)可以包含任意对象的有序集合,如x=[89,90.3,'tom']。
(2)可以通过下标索引来访问 list 中的某个元素。下标索引从左边开始时总是从 0开始,而从右边开始时,就从-1开始。序列类型的索引体系如图2-1所示。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/71_2.jpg?sign=1739333740-WnvXbB7rYMBILfZyK8anpQyNoDwvu83k-0-3d141a69f32791f46c0bd6b0f8843c4b)
图2-1 序列类型的索引体系
(3)可变长度(可任意增减元素);异质(可包含任意类型的元素);可任意嵌套(列表的元素也可以是一个列表)。
(4)支持原位改变,如x=[89,90.3,'tom'],支持x[0]=99这样的操作。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/71_3.jpg?sign=1739333740-H8edQk4MmDXvnq1WXAv8NMWW4LYtHG5h-0-60c5c0a5cfd74382bf95d78b0ddccbc3)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/72_1.jpg?sign=1739333740-hZkMAfuxb1B06IwgSJBavSMTGY8nxPbV-0-d3a3634965f9885f7fe8b2fcb63ca8f5)
注意:上例中,对列表中的元素又是一个列表的情况,可以通过 x[3]获得列表[56,89,89],接下来想对值 56 进行访问,那就按照下标索引的方式对其进行访问操作,因此获取值56的表达式为x[3][0]。
4.列表转换函数list()
通过列表转换函数list()可以将某个特定的可迭代序列转换为列表。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/72_2.jpg?sign=1739333740-RdJIfIP3VmruIlKDhTicmaEEraGfaqKw-0-3d3ffe47d8011c221688592ff3de64d8)
2.3.2 序列通用操作
列表属于可变序列,也是Python中使用频率最高、最通用的数据类型。序列类型有很多,除了列表,后面还会介绍元组及字符串。下面先介绍序列通用操作,还是以 list为例。
在讲序列的通用操作之前,先介绍列表的初始化。列表的初始化可以通过中括号来直接设定元素列表。
1.列表的初始化
列表用中括号将若干元素括起来,中间用逗号分隔。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/72_3.jpg?sign=1739333740-0uW8WtYHp6hQBbkLwEE65LXDyFigY9OM-0-23872625415892002c01ab4861a5cfd6)
2.序列的通用操作——以列表为例
序列的通用操作包括判断元素是否在序列之内、序列连接、重复序列元素等,详细说明如表2-7所示。
表2-7 序列通用操作
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/72_4.jpg?sign=1739333740-mLrKyRrxDqiLljpoN7AI0mhyrB9lMqH6-0-3097ec84f20488dc878c25814aa14745)
续表
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/73_1.jpg?sign=1739333740-aAcpAlEtNLdJgxcw3i854U7038LkaX4k-0-09ee3573b85c4038571708090e6ca455)
关于通用操作的几点说明。
1)切片操作
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/73_2.jpg?sign=1739333740-cDYYGPvg2wV1UkrfrKRvxr4nl9xCsWXJ-0-f5af2ffc36d6a7f12a2b7c2db6be6950)
观察上面的结果,当步长是一个负数时,是从后向前按步长的绝对值取出元素的,如果步长是-1,则得到的是一个置逆的结果。同样要注意的是,这里是对列表中的元素进行访问,访问操作本身并不改变列表的值。上面最后一行代码输出的结果是 2 和 4,并没有因为x[::-1]操作得到一个置逆的结果并改变原列表,所以,输出的并不是5和3。
下面用一个示例来解释列表的两个下标索引 [2]。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/73_3.jpg?sign=1739333740-IHpVAHWg0I05aHmTmjepbolDy1UaEfZI-0-6c75714a17d01e2fbbe43ce61ead32f8)
索引可以理解为是放在两个元素中间的缝隙中的,读取它的值时是读取它之后的值。这样对于范围如[0:2],表示的是0~2(包括0但不包括2)的那些元素值。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/73_4.jpg?sign=1739333740-F6HqbkICvuuTLki5y8LhHtuICImIO9S6-0-c9d43129f74eb06eca6c1163f40eb9a9)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/74_1.jpg?sign=1739333740-Go7uQ6Ca7ByjjcsHFoUb4Ntc8ri7yuXs-0-e943f82a4e32a310dd3e4fa81732d4c3)
注意:获取列表元素的值时,下标不能越界!
只有当后面右侧有内容的时候,前面的索引才会起作用。
下标索引的使用也可以从后往前即从-1 开始,但当有两个下标索引时,同样最后一个索引对象的值不包含在内。例如:
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/74_2.jpg?sign=1739333740-UWTCs3oLycUEBmnoJOEX2HoOUkpg8fLO-0-b2e6ec6ae1c39c846432e3fccd5e5aea)
以上结果表明,列表x和y并未指向同一个对象,它们不属于共享引用。
3)求和操作sum(ls)
如果想统计序列中元素的总和,可以用全局函数“sum”来实现,注意 sum 求和操作只针对序列元素是数字类型的情况。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/74_3.jpg?sign=1739333740-UPcGxAvvcQc5JtbaGgB9q0YOpwL9b1ie-0-088cf601e7d3315706ae63abc2eae5c0)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/75_1.jpg?sign=1739333740-BUqxXLRWzLf5uPSNSIJgdIacVLj5T6kK-0-6bc733fd62b83251f6529f5062e389a6)
由于列表是序列的一种,而序列除了列表还有其他的一些类型,在介绍其他的类型及列表的操作之前,应先了解所有序列通用的操作,这些操作使用都很方便。但是我们要分辨一下这些操作哪些是通过函数来完成的,哪些是通过方法来完成的。方法是与某个对象相互关联的,它的调用方式是对象名.方法名()。函数的调用方式是函数名(参数表),函数分为全局函数和来自某个模块的特定的函数[3]。
2.3.3 可变序列及列表通用操作(一)
虽然列表是可变序列,但前面以列表为例介绍的通用操作同样适用于不可变序列。
下面将继续以列表为例来介绍可变序列的通用操作。这些操作既可以作用到列表上,也可以作用到其他可变序列的数据结构上。表2-8列出了可变序列及列表的通用操作。
表2-8 可变序列及列表的通用操作(一)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/75_2.jpg?sign=1739333740-4UqcsS6fsknNrbngWYDGL0GLGhM9clzC-0-7c1aaa3a0964ff2c093d8bdb59105a77)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/75_3.jpg?sign=1739333740-M0TFLga1IEQfVDyYEhv934rMLlQ7ysP7-0-b3525f868fa4bc9387bcfa94beec81fe)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/76_1.jpg?sign=1739333740-vbkMOwNBYLITNc4pxgrVVzvmecuhlLw8-0-bc91bf62fa6959a6bad1738830727fc0)
s[:3]=100是不被允许的。因为这里有歧义,究竟是希望把前3个值删掉,然后插入一个100呢?还是要把前3个值每个都换成100呢?不能确定,因此,系统拒绝这样的操作。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/76_2.jpg?sign=1739333740-HYoHyx3TzYnPbaYSEjqq9O5tMju8aa8f-0-45e36284a147937c0bf61305da351081)
此时需要注意:如果使用开始加终止索引,并且指定了步长值,那么在给它赋值的时候,除了必须要给它一个可迭代的对象,还必须清楚它共有几个值会被替换。在上面的例子中,从第一个到最后一个,隔一个被选择的话,一共应该有 5 个值会被替换,那么在给它赋值的时候,必须给它赋 5 个值才能替换。其实在报错的消息中已经告诉我们了,报错是“值的错误”。
注意:s[i:j]=t和s[i:j:k]=t的操作是有差异的,后者要求迭代序列t的大小和s中被置换的元素个数要保持一致。
熟练掌握序列的基本操作,我们可以通过不同的手段和方式来达到同样的目的。
2.3.4 可变序列及列表通用操作(二)
本小节继续以列表为例介绍序列的通用操作(见表2-9)。
说明:复制序列copy()是一个非常重要的操作。
表2-9 可变序列及列表的通用操作(二)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/76_3.jpg?sign=1739333740-LGqtVTnkHTvNfnHFay3Sse4lATd7aGNf-0-5d3eb1c8d5b81df0e30c03dd693603b9)
将一个列表赋值给另一个列表不会产生新的列表对象。例如:
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/77_1.jpg?sign=1739333740-NUh9ua1XitAHW9UP2II9gJn5fQLi4tnx-0-66f6debb1e52e7bc45088e22e53ab87d)
当我们把列表l赋给一个新的变量s后,改变s中元素的值,结果发现原列表l中的值也被改变了,说明s和l关联的是同一个对象,这其实就是共享引用。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/77_2.jpg?sign=1739333740-RKHlof1HdJkinmoQYbgsol3mGRzLrKWE-0-9a3fdf74b9650f4b08816bcccf868ef4)
说明:全局函数id()也能检测两个对象是否是共享引用。
但这个结果可能并不是我们想要的。因为开始列表 l,是一个独立的列表,以后要单独对它进行操作。产生一个新的列表s,只不过要求列表s的值与l相同,但并不希望它们指向同一个对象。有两种解决这个问题的方法:
方法一:利用切片操作取出列表的所有元素赋值给新的变量。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/77_3.jpg?sign=1739333740-nkCe3J3w27Qh2rs0TPaXupWF9dF3fHhx-0-cdf0b710062e2ba20749fd3d71374169)
“s=l[:]”这种方式虽然使得s和l的值相同,但它们并没有指向同一个对象。这就是前面讲到的,l[:]和 l 看似结果相同(获取所有元素的值),但实际上它们的作用是不一样的。
对于“s=l[:]”这样的操作方式,l和s 不属于共享引用,当改变一个列表中某个元素的值时,另一个列表中相应位置的元素不会改变。
方法二:利用列表的方法“copy()”将列表的所有元素复制到新的变量中。
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/77_4.jpg?sign=1739333740-NA1ggEWY4c6qABfKBg3vErlJANh6tV5h-0-37767c83aee47881119ce67ffbec793b)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/78_1.jpg?sign=1739333740-LfSWn8WvwuVqEUX9UGZKhX4sYOn7Ds62-0-e4fb8a5496afec36d931009f872de714)
注意:关于这一点很容易出错,请一定弄清楚。要正确理解第 2 章介绍的赋值逻辑,今后在介绍函数参数传递时经常会遇到这种情景。
以上介绍的这些操作属于可变序列中的操作,由于列表是使用频率最高的一个可变序列,所以,目前以列表为例进行讲解,后面用到其他的可变序列时,这些通用操作也是支持的。接下来看有关列表所特有的一个操作。
列表中元素的顺序与赋值时元素的顺序是一致的,尽管可以通过“reverse()”方法进行反转,但有时可能需要按照给定的条件进行排序,这时可以使用列表自带的一个方法“sort()”来实现(见表2-10)。
表2-10 列表特有的操作
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/78_2.jpg?sign=1739333740-QrUJUkyaezYblMA2mkktJ57vMVJKBP9j-0-4e735c6cfa785bcdce963e945a925cc2)
关于列表持有操作的几点说明如下:
1)要求列表中的元素必须具有相同的类型
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/78_3.jpg?sign=1739333740-O8tNIAWKQ88q7b93uD9LEl6wF9PhK49o-0-93ba84f1def7b72c013f89295725c992)
若一个列表含有数值、字符串等混合类型,则不能进行排序操作。
2)方法“sort()”和全局函数“sorted()”的区别
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/78_4.jpg?sign=1739333740-nPfz8Agyib41wanXpJ7G8BcixtsVp2lf-0-543d3bdf9158c724dfb5937f64daeb3d)
“sort()”方法没有结果返回,只是改变原列表的排列顺序。sorted()是全局函数,有结果返回,但原序列本身未改变。同时,全局函数不依附于任何的对象,因此,必须把序列以参数的形式传递给它。
3)方法“sort()”和全局函数sorted()的使用示例(学习了lambda函数后再来看下面的例子)
![](https://epubservercos.yuewen.com/ED6D45/13898202805418106/epubprivate/OEBPS/Images/79_1.jpg?sign=1739333740-z72AzDnH8KqPmPbOAonbY36NCOGL6FUQ-0-c51858221ebf16c09710a56b84e5ef73)
在调用每个函数的时候,尽量要做到心中有数,究竟是属于整个序列的通用操作,还是属于可变序列的通用操作,或者是某一个特定的类型所带来的函数或方法,这样才能灵活使用。