购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第4章
CHAPTER 4
列表和元组

在本章中将学习列表的基本操作,包括通过索引操作列表中的元素;列表的加法和列表的乘法;修改列表元素;用于操作列表的核心方法;列表元素迭代等。除此之外,还会了解到列表的一个近亲:元组,这是一类只读的列表。列表元素的类型和数量可以是任意的,在列表中,可能只有几个元素,也可能有成千上万个元素,每个元素的类型可能是相同的,也可能是完全不同的。总之,列表是一种非常灵活的数据结构。

4.1 定义列表

代码位置:src/sequence/create_list.py

Python中的列表就是一组值的列表,这些值可以是同一个类型的,也可以是不同类型的。列表中的所有值需要被包含在一对中括号([…])中,多个值之间用逗号分隔:

字符串类型列表(列表中每个值都是字符串类型):

     names=["Bill", "Mary", "Jack"]

整数类型列表(列表中每个值都是整数类型):

     numbers=[1,2,3,4,5]

混合类型的列表(列表中每个值的类型可能是不同的):

     values=["Bill", 30,12.5, True]

4.2 列表的基本操作

微课视频

本节主要介绍与列表相关的基本操作,如获取列表元素、分片、列表的加法、列表的乘法、列表封包和列表解包等。

4.2.1 通过索引操作列表元素

列表中的所有元素都有编号,编号从0开始,逐渐递增,如0、1、2、3等。列表中的所有元素都可以通过编号访问,这个编号被称为索引。

下面的代码获取了names列表中的第1个和第3个元素的值,并输出这些元素值。

代码位置:src/sequence/access_list1.py

在前面的代码中,通过索引0和索引2,分别获取了names列表中的第1个和第3个元素值。Python语言中的字符串也可以通过索引获取特定的字符。

代码位置:src/sequence/access_list2.py

这段代码通过索引获取并输出字符串s中的第1个和第4个字符,以及获取Apple的第3个字符。

还可以通过索引从输入的字符串中截取某一个字符。下面的代码从终端输入一个年份(必须是4位的年,如2021),如果只对年份的最后一个字符感兴趣,获取使用索引截取年份的最后一位数字。

代码位置:src/sequence/year.py

如果索引是0或正整数,那么Python会从列表左侧第1个元素开始取值,如果索引是负数,那么Python会从列表右侧第1个元素开始取值。列表最后一个元素的索引是-1,倒数第2个元素的索引是-2,以此类推。

下面的代码通过正索引获取names列表中的第1个元素值,以及通过负索引获取names列表中倒数第1个和倒数第2个元素值。

代码位置:src/sequence/index.py

当索引超过列表的索引范围时,会抛出异常。下面的代码使用索引4和-4引用names列表中的元素值,这两个索引都超出了names列表的索引范围,所以会抛出异常。不过当第1个异常抛出时,后面的语句都不会执行了。

代码位置:src\sequence\index_exception.py

程序运行结果如图4-1所示。

图4-1 索引超出names列表的范围,将导致抛出异常

4.2.2 分片

微课视频

分片(Slicing)操作可以从列表A中获取一个子列表B。从A中获取B,需要指定B在A中的开始索引和结束索引,因此,分片操作需要指定两个索引。

由于字符串可以看作字符的列表,所以可以用列表的这个分片特性截取子字符串。

代码位置:src/sequence/slicing.py

在上面的代码中,使用url[8:15]截取url中的"geekori",其中8和15是url中的两个索引。可以看到,两个索引之间要使用冒号(:)分隔。可能有的读者会发现,索引15并不是"i"的索引,而是"."的索引,没错,在指定子列表结束索引时,要指定子列表最后一个元素的下一个元素的索引,因此,应该指定"."的索引,而不是"i"的索引。

那么如果子列表的最后一个元素恰好是父列表的最后一个元素该怎么办呢?例如,url中的最后一个元素是"m",如果要截取"geekori.com",子列表的结束索引应该如何指定呢?其实子列表的结束索引只要指定父列表最后一个元素的索引加1即可。由于父索引最后一个元素"m"的索引是18,因此,要截取"geekori.com",需要指定结束索引为19,也就是url[8:19]。

分片中使用的索引同样可以是负值,如下面的代码截取了numbers中的不同的子列表。

代码位置:src/sequence/sub_sequence.py

如果将结束索引设为0,会获取一个空列表。

代码位置:src\sequence\null_sequence.py

Python语言规定,如果结束索引在列表中的位置不大于开始索引在列表中的位置,那么就会返回空列表,在这里索引0比索引-3的位置小。如果要使用负数作为索引,并且获取的子列表的最后一个元素与父列表的最后一个元素相同,那么可以省略结束索引,或结束索引直接用列表长度值即可。

代码位置:src/sequence/negative_index.py

如果省略了开始索引,那么会截取结束索引前面所有的值。

代码位置:src/sequence/omit_start_index.py

如果开始索引和结束索引都不指定,那么会复制整个子列表。

代码位置:src/sequence/copy_sequence.py

在对列表分片时,默认的步长是1,也就是说,获取的子列表的元素都是相邻的。如果要获取不相邻的元素,就需要指定步长。例如,要获取索引为1、3、5的元素作为子列表的元素,就需要将步长设为2。

代码位置:src/sequence/step_sub_sequence.py

在上面代码中,使用numbers[1:6:2]获取了索引为1、3、5的元素作为子列表的元素,其中2是步长,可以看到,开始索引、结束索引和步长之间都用冒号(:)分隔。

实际上开始索引、结束索引和步长都是可以省略的。下面的代码在分片时指定步长,但省略了开始索引以及结束索引。

代码位置:src/sequence/omit_start_end_index.py

步长不能为0,但可以是负数。如果步长为0,会抛出异常,如果步长是负数,分片会从列表的右侧开始,这时开始索引要大于结束索引。

代码位置:src/sequence/negative_zero.py

图4-2 步长为0,抛出异常

程序运行结果如图4-2所示。

在上面代码中,如果步长为负数,那么分片的开始索引需要大于结束索引。例如,numbers[8:2:-2]表示从索引为8的元素开始,往前扫描,直到索引为2的元素的上一个元素,也就是索引为3的元素为止。

当然,如果使用负数作为步长,还有一些比较复杂的用法,得出这些用法的分片结果,需要动一下脑筋。

代码位置:src/sequence/negative_step.py

4.2.3 列表的加法

微课视频

列表也可以相加,但要注意,这里的相加,并不是相对应的列表元素值相加,而是列表首尾相接。由于字符串属于字符列表,所以字符串相加也可以看作列表相加。但一个字符串不能和一个列表相加,否则会抛出异常。

代码位置:src/sequence/add_list.py

图4-3 列表的加法

程序运行结果如图4-3所示。

可以看到,上面代码在运行最后一条语句时会抛出异常,原因是列表和字符串相加。而要想让"hello"和列表相加,需要将"hello"作为列表的一个元素,如["hello"],然后再和列表相加。两个相加的列表元素的数据类型可以是不一样的,例如,上面代码中第3行将一个整数类型的列表和一个字符串类型的列表相加,这两个列表会首尾相接连在一起。

4.2.4 列表的乘法

微课视频

如果用数字n乘以一个列表会生成新的列表,而在新的列表中,原来的列表将被重复n次。如果列表的值是None(Python语言内建的一个值,表示"什么都没有"),那么将这个列表与数字n相乘,假设这个包含None值的列表长度是1,那么就会产生占用n个元素空间的列表。

代码位置:src/sequence/multi_list.py

     # 字符串与数字相乘,运行结果:hellohellohellohellohello
     print('hello' * 5)
     # 列表与数字相乘,运行结果:[20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
     print([20] * 10)
     # 将值为None的列表和数字相乘,运行结果:[None, None, None, None, None, None]
     print([None] * 6)

4.2.5 in运算符

微课视频

为了检查某个值是否属于一个列表,可以使用in运算符。这个运算符是布尔运算符,也就是说,如果某个值属于一个列表,那么in运算符返回True,否则返回False。

下面的例子使用in运算符判断一个字符串是否属于另一个字符串,以及一个值是否属于一个列表。

代码位置:src/sequence/in_list.py

在上面代码中,通过in运算符,检查了"you"和"hello"是否在str中,很显然,str包含"you",而"hello"并不属于str,所以前者返回True,后者返回False。接下来检查"Mike"和"Mary"是否属于names列表,很明显,"Mike"是列表names的第2个元素,而"Mary"并不是列表names的元素,所以前者返回True,后者返回False。

4.2.6 列表的长度、最大值和最小值

微课视频

本节会介绍3个内建函数:len、max和min。这3个函数用于返回列表中元素的数量、列表中值最大的元素和值最小的元素。使用max和min函数要注意一点,就是列表中的每个元素值必须是可比较的,否则会抛出异常,例如,如果列表中同时包含整数和字符串类型的元素值,那么使用max和min函数将抛出异常。

下面的例子测试了len、max和min函数的用法,在使用max和min函数时,如果函数参数指定了不同类型的列表或值,并且这些值无法比较,将抛出异常。

代码位置:src/sequence/len_max_min.py

图4-4 测试len、max和min函数的用法

程序的运行结果如图4-4所示。

从上面的代码中可以看出,max函数和min函数的参数不仅可以是一个列表,还可以是可变参数,这两个函数会返回这些参数中的最大值和最小值。不管max和min函数的参数是一个列表,还是可变参数,每个值都必须是可比较的,否则会抛出异常。

微课视频

4.2.7 列表封包和列表解包

列表封包(Packing)是指将多个离散的值组成列表,而列表解包(Unpacking)恰恰相反,是将列表拆成离散的值。也就是说,列表封包和列表解包,就是指将多个值组成一个列表(封包)或将一个列表拆成多个值(解包)。其他任何编程语言都可以完成列表封包和列表解包的操作,只是Python将这些功能作为语言的一部分提供,而不是使用循环这种原始的方式从列表中一个一个地提取元素值。所以通过Python实现这些功能更容易。

列表封包和列表解包有如下几种形式。

1.直接将值赋给列表变量
     values = 10,20,30

在上面代码中,等号(=)右侧有3个整数值,并用逗号分隔,这时values变量就会变成一个列表变量。

如果要解包,就需要执行逆过程,代码如下:

     x,y,z = values

在上面代码中,values是前面生成的包含10、20、30的列表,而x、y、z是3个变量,如果右侧是一个列表,左侧是一组变量,那么这个赋值过程就叫作列表解包。Python解析器会将values列表中的每个值分别赋给等号(=)左侧的相应变量。但要注意,列表中元素的个数要与左侧变量数量相同,否则会抛出异常。

2.通配符匹配

前面提到过,在列表解包时,变量的数量必须与列表中元素的个数相同,否则会抛出异常。但是这种规则有一个问题,就是如果列表中元素个数非常多时,如有100个,岂不是要提供100个变量才可以解包。其实并非如此,如果只想将列表的部分元素解包,其他的元素作为原列表的子列表,可以使用通配符,也就是一个星号(*),看下面的代码:

     a,b,*c = [1,2,3,4,5,6,7,8]

在上面代码中,右侧的列表有8个元素,而左侧只有3个变量,如果按以前的逻辑,执行这行代码肯定是要抛出异常的,但由于c变量前面有一个星号(*),这就是通配符,如果放在一个变量前面,表示列表,也就是说,变量c本身就是一个列表。Python解析器首先会按列表中元素的顺序和匹配对应的单个变量,剩下的值都给列表变量,也就是c。所以执行这行代码后,a和b的值分别是1和2,而c的值是[3,4,5,6,7,8]。

要注意,等号左侧的变量组中只能有一个带星号的变量,否则Python解析器无法确定将剩下的列表元素的哪些部分匹配给哪个子列表变量。所以执行下面的代码会抛出异常。

如果等号左右都是变量,并且数量相等,也可以互相赋值,通过这个特性,可以非常容易地交换变量的值(不需要临时变量)。

下面的案例完整地演示了列表封包和列表解包的用法。

代码位置:src/sequence/packing_unpacking.py

图4-5 列表封包和列表解包的用法

程序运行结果如图4-5所示。

4.2.8 修改列表元素

微课视频

如果要修改列表中的某个元素,与使用列表元素一样,要使用索引,并将索引放到一对中括号中。下面的例子修改了列表s中的前两个元素值。

代码位置:src/sequence/modify_list.py

在上面的代码中,通过列表的元素赋值操作,修改了列表s中的前两个元素,第1个元素修改成了"Mary",第2个元素修改成了20。

在列表元素赋值的操作中,列表索引可以是负数,在这种情况下,会从列表最后一个元素开始算起。例如,s[-1]表示倒数第1个列表元素,s[-2]表示倒数第2个列表元素。不管列表索引使用正数还是负数,都不能超过索引范围,否则会抛出异常。

代码位置:src/sequence/list_overflow.py

程序运行结果如图4-6所示。

图4-6 列表赋值操作索引超出范围,抛出异常

4.2.9 删除列表元素

从列表中删除元素也很容易,使用del语句就可以做到。

4.2.10 分片赋值

微课视频

分片赋值和分片获取子列表一样,也需要使用分片操作,也就是需要指定要操作的列表的范围。

下面的例子将利用分片赋值将列表中的子列表替换成其他列表。并使用list函数将字符串分解成由字符组成的列表,并替换字符串中的某一部分。

代码位置:src/sequence/slicing_assignment.py

上面的代码使用了分片赋值对原列表进行了赋值操作,可以看到,分片赋值是用另一个列表修改原列表中的子列表。也就是将原列表中的子列表替换成另外一个子列表。而且在赋值时,被替换的子列表和新的子列表可以不等长。例如,["world","yeah"]可以被替换为['a', 'b', 'c']。

可能有很多读者会想到,可以利用这个特性在列表中插入一个列表,或删除一些列表元素。

代码位置:src/sequence/slicing_insert_remove.py

在上面的代码中,使用分片赋值操作在列表numbers中的1和6之间插入了列表[2,3,4,5]。numbers[1:1]中冒号(:)前面的数字表示要替换的子列表的第1个元素在父列表中的索引,而冒号后面的数字表示子列表下一个元素在父列表中的索引,所以冒号前后两个数字相等,表示不替换列表中的任何元素,直接在冒号前面的数字表示的索引的位置插入一个新的列表。最后使用分片赋值将第2、3、4个元素值替换成了空列表,所以最后numbers列表的值是[1,5,6,7]。

4.3 列表方法

· append:在列表最后插入新的值。

· clear:用于清除列表的内容。

· copy:用于复制列表。

· count:用于统计某个元素在列表中出现的次数。

· extend:用于在列表结尾插入另一个列表,也就是用新列表扩展原有的列表。有点类似列表相加,不过extend方法改变的是被扩展的列表,而列表相加产生了一个新列表。

· index:用于从列表中找出某个值第一次出现的索引位置。

· insert:用于将值插入列表的指定位置。

微课视频

在列表中定义了很多方法 ,用于操作列表中的数据以及列表本身,本节将给出一个完整的案例讲解这些方法的核心功能。

下面是列表中定义的一些常用方法。

· pop:用于移除列表中的元素(默认是最后一个元素),并返回该元素的值。

· remove:用于移除列表中某个值的第一次匹配项。

· reverse:用于将列表中的元素反向存放。

· sort:用于对列表进行排序,调用该方法会改变原来的列表。

下面的例子完整地演示了如何使用这些方法操作列表。

代码位置:src/sequence/list_methods.py

4.4 元组

微课视频

元组与列表一样,也是一种列表。唯一的不同是元组不能修改,也就是说,元组是只读的。定义元组非常简单,只需要用逗号(,)分隔元素值即可。

当然,也可以将元组用一对圆括号括起来。

既然元组中的元素值是用逗号分隔的,那么如何定义只有一个元素的元组呢?当然也是在一个值后面加逗号了。

如果要创建一个空元组(没有任何元素的元组),可以直接用一对圆括号。

如果想将列表转换为元组,可以使用tuple函数。

下面的例子演示了如何创建元组,以及如何生成5个同样值的元组,最后使用tuple函数将列表和字符串转换为元组。

代码位置:src/sequence/tuple_demo.py

可能有很多读者感到奇怪,Python语言为什么要加入元组呢?如果要只读的列表,直接用列表不就得了,不修改列表不就相当于只读的了。如果从技术上来说,这么做是可行的。但有如下两个重要原因,让我们必须使用元组。

(1)元组可以在映射中作为键值使用,而列表不能这么用。关于映射的内容会在后面的章节详细介绍。

(2)很多内建函数和方法的返回值就是元组,也就是说,如果要使用这些内建的函数和方法的返回值,就必须使用元组。

4.5 for循环与列表

本节将详细介绍for循环与列表的使用方法,其中包括使用for循环枚举列表与元组中的元素,以及使用for表达式自动生成列表等。

4.5.1 使用for循环枚举列表与元组中的元素

微课视频

如果想枚举列表或元组中的全部元素或部分元素,可以使用循环语句,for循环和while循环都可以,本节主要讲解如何使用for循环枚举列表与元组中的元素,while循环枚举列表与元组中元素的方式与for循环类似。

本节以列表为例(元组类似),要想用for循环枚举列表中的元素,首先需要获取列表的长度,可以使用len函数完成这个功能,获取了列表的长度后,就可以通过索引来引用列表中的特定元素了。

下面的例子演示了如何通过for循环和索引枚举列表中的所有元素。

代码位置:src/sequence/for_list.py

程序运行结果如图4-7所示。

本例使用了一个range函数,该函数返回一个可以迭代的整数区间,是半闭半开区间。例如,range(0,10)就表示0<= i <10。

图4-7 使用for循环和索引枚举列表中的所有元素

微课视频

4.5.2 使用for-in循环遍历列表与元组中的元素

for-in循环同样可以遍历列表与元组中的所有元素,因为列表与元组本身就是可以迭代的。for-in循环的基本使用方式如下:

其中a_list是一个列表变量,换成元组也可以。要注意,使用for-in循环遍历列表与元组的方式无法直接获取当前索引(除非添加计数变量)。

下面的例子演示了如何通过for-in循环遍历列表中的所有元素。

代码位置:src/sequence/for_in_list.py

图4-8 使用for-in循环遍历列表中的所有元素

程序运行结果如图4-8所示。

4.5.3 使用for表达式自动生成列表

微课视频

如果列表中的元素有一定的规则,就可以通过for循环自动生成列表中的元素,不过这样代码比较冗长,所以推荐使用for表达式自动生成列表。在Python语言中,for表达式可以与列表结合,看下面代码:

     my_list = [x for x in range(10)]

上面代码生成的列表是[0,1,2,3,4,5,6,7,8,9]。第1个x相当于列表元素的计算表达式,第2个x是for表达式中的自变量。当for表达式每循环一次时,就会向列表中添加一个元素,而这个元素的值就是第1个x,那么这个x的值就是for表达式中的x赋予的。所以这行代码与下面的代码等价:

下面的例子演示了如何通过for表达式自动生成列表。

代码位置:src/sequence/for_expr1.py

图4-9 使用for表达式自动生成列表

程序运行结果如图4-9所示。

在本例中,num_list1使用了一对圆括号,这并不能创建元组,而是创建了一个生成器(generator),关于生成器的内容会在后面的章节详细介绍,现在只要知道生成器是可以迭代的就可以了。使用for表达式不能创建元组,只能创建列表和生成器。

下面的例子演示了如何通过for表达式自动生成更复杂的列表。

代码位置:src/sequence/for_expr2.py

     num_range = range(10)
     # 列表的每个元素是两个x的和
     num_list = [x + x for x in num_range if x % 2 == 1]
     # 运行结果:[2, 6, 10, 14, 18]
     print(num_list)

for表达式支持多个变量,也就是说,生成的列表元素本身可以是一个非常复杂的表达式,最终生成的列表元素是这个表达式的计算结果,而且表达式中可以有任意多个变量,只不过for表达式中的循环层次要与变量个数相同。也就是说,表达式如果有两个变量,那么for表达式需要是二重循环。

下面的例子演示了如何在for表达式中使用多变量生成列表。

代码位置:src/sequence/for_expr3.py

程序运行结果如图4-10所示。

图4-10 在for表达式中使用多变量生成列表

4.6 实战与演练

1.编写Python程序,通过Python控制台输入若干整数,直到输入end结束输入(可以使用while或for循环),在输入的过程中,将输入的每个整数追加到numbers列表中,然后对numbers列表进行降序排列,最后输出numbers列表的元素值。

运行程序后,输出结果如图4-11所示。

答案位置: src/sequence/solution1.py

2.编写Python程序,创建两个列表:numbers1和numbers2,将numbers1中的索引1~3的元素值追加到numbers2列表的结尾,然后对numbers2中的元素值进行升序排列,最后输出numbers中的所有元素值。

答案位置: src/sequence/solution2.py

3.编写Python程序,通过Python终端输入一个大于1的整数n,然后产生一个二维列表。二维列表的尺寸是n * n。每个列表元素的值为1~n * n,依次排列。例如,输入的整数是3,会产生如下的二维列表。

     [1,2,3]
     [4,5,6]
     [7,8,9]

产生完列表后,会互换二维列表中的行列元素值。如将上面的二维列表互换行列值的结果如下:

     [1,4,7]
     [2,5,8]
     [3,6,9]

答案位置: src/sequence/solution3.py

4.输入年、月、日,并将月转换为中文输出,如输入的月份是4,要求输出"四月"。程序运行结果如图4-12所示。

图4-11 输入列表中的元素

图4-12 将月转换为中文输出

答案位置: src/sequence/solution4.py

5.从Python控制台输入一个Url和一个数字n。然后对Url分片,获取Url的scheme和host。

程序运行结果如图4-13所示。

答案位置: src/sequence/solution5.py

6.从Python终端输入一个整数n,然后生成包含1~n-1个数字的列表,最后使用分片挑出列表中所有的奇数和偶数,并输出这些数。运行效果如图4-14所示。

图4-13 拆分Url的运行结果

图4-14 挑出奇数和偶数的运行效果

答案位置: src/sequence/solution6.py

7.利用列表的乘法生成一个6×11二维的列表,列表的每个元素是一个一维的列表,列表中的每个元素是空格或星号(*),二维列表中的元素会形成一个由星号(*)组成的正三角形。程序运行效果如图4-15所示。

图4-15 包含正三角形的二维列表

答案位置: src/sequence/solution7.py

8.从控制台输入用户名和密码,并通过in运算符在account列表中查找是否存在输入的用户名和密码,如果存在,则输出"登录成功",如果不存在,则输出"登录失败"。

答案位置: src/sequence/solution8.py

4.7 本章小结

在本章中学习了关于列表的各种知识,这些知识中最重要,也是最常用的是对列表元素的增、删、改、查,列表元素迭代、分片以及列表的连接与排序。所以读者在学习到这些知识点时应格外注意。另外,在使用列表时,也不是一帆风顺的,有时也会抛出异常,最常见的异常原因是列表索引越界,所以读者在使用列表时要注意这点。 azORxZ+u8khhsQ9Eh+MQ+MlKrxyBRXdQfJfAZEhdE9Jkx1r5sq7Ut8SHHjX8jQeZ

点击中间区域
呼出菜单
上一章
目录
下一章
×