第5章 列表、元组和字符串
5.1 列表:一个“打了激素”的数组
视频讲解
有时候可能需要将一些相互之间有关联的数据保存到一起,很多接触过编程的读者脑海里浮现出来的第一个概念应该就是数组。数组允许把一些相同类型的数据挨个儿摆在一起,然后通过下标进行索引。
Python也有类似数组的东西,不过更为强大。由于Python的变量没有数据类型,所以Python的“数组”可以同时存放不同类型的变量。这么厉害的东西,Python将其称为列表,姑且可以认为列表就是一个“打了激素”的数组。
5.1.1 创建列表
创建一个列表非常简单,只需要使用中括号将数据包裹起来(数据之间用逗号分隔)就可以了。
>>> [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
上面创建了一个匿名的列表,因为没有名字,所以创建完也没办法再次使用它。为了可以随时对它进行引用和修改,可以给它贴上一个变量名:
注意:
type()函数用于返回指定参数的类型,list即列表的意思。
没有哪一项规定要求Python的列表保存同一类型的数据,因此,它支持将各种不同的数据存放到一起:
>>> mix = [520, "小甲鱼", 3.14, [1, 2, 3]]
可以看到这个列表里有整型、字符串、浮点型数据,甚至还可以包含另一个列表。
当实在想不到要往列表里面塞什么数据的时候,可以先创建一个空列表:
>>> empty = []
5.1.2 向列表添加元素
列表并不是一成不变的,可以随意地往里面添加新的元素。添加元素到列表中,可以使用append()方法:
>>> number = [1, 2, 3, 4, 5] >>> number.append(6) >>> number [1, 2, 3, 4, 5, 6]
可以看到,数字6已经被添加到列表number的末尾了。有读者可能会问,这个append()的调用怎么跟平时的BIF内置函数调用不一样呢?
因为append()并不是一个BIF,它是属于列表对象的一个方法。中间这个“.”,暂时可以理解为范围的意思:append()这个方法是属于一个叫number的列表对象的。关于对象的知识,暂时只需要理解这么多,后面小甲鱼会再详细地来介绍对象。
下面代码试图将数字8和9同时添加进number列表中:
出错了,这是因为append()方法只支持一个参数。
如果希望同时添加多个数据,可以使用extend()方法向列表末尾添加多个元素:
>>> number.extend([8, 9]) >>> number [1, 2, 3, 4, 5, 6, 8, 9]
注意:
extend()事实上是使用一个列表来扩充另一个列表,所以它的参数是另一个列表。
无论是append()还是extend()方法,都是往列表的末尾添加数据,那么是否可以将数据插入到指定的位置呢?
当然没问题,想要往列表的任意位置插入元素,可以使用到insert()方法。
insert()方法有两个参数:第一个参数指定待插入的位置(索引值),第二个参数是待插入的元素值。
下面代码将数字0插入到number列表的最前面:
>>> number.insert(0, 0) >>> number [0, 1, 2, 3, 4, 5, 6, 8, 9]
在计算机编程中常常会出现一些“反常识”的知识点,如在Python列表中,第一个位置的索引值是0,第二个是1,第三个是2,以此类推……
下面代码将数字7插入到6和8之间:
>>> number.insert(7, 7) >>> number [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
insert()方法中代表位置的第一个参数还支持负数,表示与列表末尾的相对距离:
>>> number.insert(-1, 8.5) >>> number [0, 1, 2, 3, 4, 5, 6, 7, 8, 8.5, 9]
5.1.3 从列表中获取元素
视频讲解
通过索引值可以直接获取列表中的某个元素:
>>> eggs = ["鸡蛋", "鸭蛋", "鹅蛋", "铁蛋"] >>> eggs[0] '鸡蛋' >>> eggs[3] '铁蛋'
如果想要访问列表中最后一个元素,怎么办?可以使用len()函数获取该列表的长度(元素个数),再减1就是这个列表最后一个元素的索引值:
>>> eggs = ["鸡蛋", "鸭蛋", "鹅蛋", "铁蛋"] >>> eggs[len(eggs)-1] '铁蛋' >>> eggs[len(eggs)-2] '鹅蛋'
len()函数的调用直接省去也可以实现同样的效果,即当索引值为负数时,表示从列表的末尾反向索引:
如果要将“鸭蛋”和“铁蛋”的位置进行调换,通常可以这么写:
这里的temp是一个临时变量,避免相互覆盖。不过Python允许适当地“偷懒”,下面代码可以实现相同的功能:
有时候可能需要开发一个具有“抽奖”功能的程序,只需要先将“奖项/参与者”放到列表里面,然后配合random模块即可实现:
random的choice()方法可以从一个非空的序列(如列表)中随机获取一个元素。
列表中还可以包含另一个列表,如果要获取内部子列表的某个元素,应该使用两次索引:
5.1.4 从列表删除元素
从列表中删除元素,可以有三种方法实现:remove()、pop()和del。
remove()方法需要指定一个待删除的元素:
使用remove()删除元素,并不需要知道这个元素在列表中的具体位置。但是如果指定的元素不存在于列表中,程序就会报错:
pop()方法是将列表中的指定元素“弹”出来,也就是取出并删除该元素的意思,它的参数是一个索引值:
如果不带参数,pop()方法默认是弹出列表中的最后一个元素:
最后一个是del语句,注意,它是一个Python语句,而不是del列表的方法,或者BIF:
del语句在Python中的用法非常丰富,不仅可以用来删除列表中的某个(些)元素,还可以直接删除整个变量:
分析:上面代码由于eggs整个变量被del语句删除了,所以再次引用时,Python由于找不到该变量,便会报错。
5.1.5 列表切片
切片(slice)语法的引入,使得Python的列表真正地走向了高端。这个连Python之父都爱不释手的语法真有那么神奇吗?不妨来试一试。
现在要求将列表list1中的三个元素取出来,放到列表list2里面。学了前面的知识,可以使用“笨”方法来实现:
像这样,从一个列表中取出部分元素是非常常见的操作,但这里是取出三个元素,如果要求取出列表中最后200个元素,那不是很心酸?
其实动动脑筋还是可以实现的:
虽然可以实现,但是每次都要套个循环跑一圈,未免也太烦琐了!切片的引入,大大地简化了这种操作:
很简单对吧?只不过是用一个冒号隔开两个索引值,左边是开始位置,右边是结束位置。这里要注意的一点是:结束位置上的元素是不包含的(如上面例子中,“神奇女侠”的索引值是4,如果写成list1[2:4],便不能将其包含进来)。
使用列表切片也可以“偷懒”,之前提到过Python是以简洁而闻名于世,所以你能想到的“便捷方案”,Python的作者以及Python社区的小伙伴们都已经想到了,并付诸实践,你要做的就是验证一下是否可行:
如果省略了开始位置,Python会从0这个位置开始。同样道理,如果要得到从指定索引值到列表末尾的所有元素,把结束位置也省去即可。如果啥都没有,只有一个冒号,Python将返回整个列表的拷贝。
这种方法有时候非常方便,如想获取列表最后的几个元素,可以这么写:
注意:
列表切片并不会修改列表自身的组成结构和数据,它其实是为列表创建一个新的拷贝(副本)并返回。
5.1.6 进阶玩法
列表切片操作实际上还可以接受第三个参数,其代表的是步长,默认值为1。下面将步长修改为2,看看有什么神奇的效果?
其实该代码还可以直接写成list1[::2],实现效果是一样的。
如果将步长设置为负数,如-1,结果会是怎样呢?
>>> list1[::-1] [9, 8, 7, 6, 5, 4, 3, 2, 1]
这就很有意思了,将步长设置为-1,相当于将整个列表翻转过来。
上面这些列表切片操作都是获取列表加工后(切片)的拷贝,并不会影响到原有列表的结构:
但如果将del语句作用于列表切片,其结果又让人大跌眼镜:
>>> del list1[::2] >>> list1 [2, 4, 6, 8]
是的,del直接作用于原始列表了,因为不这样做的话,代码就失去意义了,不是吗?同样会作用于原始列表的操作还有为切片后的列表赋值:
>>> list1 = ["钢铁侠", "蜘蛛侠", "蝙蝠侠", "绿灯侠", "神奇女侠"] >>> list1[0:2] = ["超人", "闪电侠"] >>> list1 ['超人', '闪电侠', '蝙蝠侠', '绿灯侠', '神奇女侠']
5.1.7 一些常用操作符
视频讲解
此前学过的大多数操作符都可以运用到列表上:
>>> list1 = [123] >>> list2 = [234] >>> list1 > list2 False >>> list1 <= list2 True >>> list3 = ['apple'] >>> list4 = ['pineapple'] >>> list3 < list4 True
列表好像挺聪明的,不仅懂得比大小,还知道菠萝(pineapple)比苹果(apple)大?那如果列表中不止一个元素呢?结果又会如何?
>>> list1 = [123, 456] >>> list2 = [234, 123] >>> list1 > list2 False
怎么会这样?Python做出这样的判断是基于什么根据呢?总不会是随机瞎猜的吧?
list1列表两个元素的和是579,按理应该比list2列表的和357要大,那为什么list1>list2还会返回False呢?
其实,Python的列表原来并没有我们想象中那么“智能”,当列表包含多个元素的时候,默认是从第一个元素开始比较,只要有一个PK赢了,就算整个列表赢了。字符串比较也是同样的道理(字符串比较的是每一个字符对应的ASCII码值的大小)。
前面演示过字符串可以使用加号(+)进行拼接,使用乘号(*)来实现自我复制。这两个操作符也可以作用于列表:
>>> list1 = [123, 456] >>> list2 = [234, 123] >>> list3 = list1 + list2 >>> list3 [123, 456, 234, 123]
加号(+)也叫连接操作符,它允许把多个列表对象合并在一起,其实就相当于extend()方法实现的效果。一般情况下建议使用extend()方法来扩展列表,因为这样显得更为规范和专业。另外,连接操作符并不能实现列表添加新元素的操作:
乘号(*)也叫重复操作符,重复操作符同样可以用于列表中:
>>> list1 = ["FishC"] >>> list1 * 3 ['FishC', 'FishC', 'FishC']
另外有个成员关系操作符大家也不陌生了,我们是在谈for循环的时候认识它的,成员关系操作符就是in和not in:
>>> list1 = ["小猪", "小猫", "小狗", "小甲鱼"] >>> "小甲鱼" in list1 True >>> "小乌龟" not in list1 True
之前说过列表里边可以包含另一个列表,那么对于列表中的列表的元素,能不能使用in和not in测试呢?试试便知:
>>> list1 = ["小猪", "小猫", ["小甲鱼", "小乌龟"], "小狗"] >>> "小甲鱼" in list1 False >>> "小乌龟" not in list1 True
可见in和not in只能判断一个层次的成员关系,这跟break和continue语句只能跳出一个层次的循环是一个道理。
在开发中,有时候需要去除列表中重复的数据,只要利用好in和not in,就可以巧妙地实现:
分析:代码先迭代遍历old_list的每一个元素,如果该元素不存在于new_list中,便调用列表的append()方法添加进去。
5.1.8 列表的小伙伴们
接下来认识一下列表的小伙伴们,列表有多少小伙伴呢?不妨让Python自己告诉我们:
产生了一个熟悉又陌生的列表,很多熟悉的方法似曾相识,如append()、extend()、insert()、pop()、remove()都是学过的。下面再给大家介绍几个常用的方法。
count()方法的作用是统计某个元素在列表中出现的次数:
>>> list1 = [1, 1, 2, 3, 5, 8, 13, 21] >>> list1.count(1) 2
index()方法的作用是返回某个元素在列表中第一次出现的索引值:
>>> list1.index(1) 0
index()方法可以限定查找的范围:
>>> start = list1.index(1) + 1 >>> stop = len(list1) >>> list1.index(1, start, stop) 1
reverse()方法的作用是将整个列表原地翻转:
>>> list1 = [1, 1, 2, 3, 5, 8, 13, 21] >>> list1.reverse() >>> list1 [21, 13, 8, 5, 3, 2, 1, 1]
sort()方法的作用是对列表元素进行排序:
>>> list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0] >>> list1.sort() >>> list1 [0, 1, 2, 3, 5, 6, 8, 9, 10]
那如果需要从大到小排队呢?很简单,先调用sort()方法,列表会先从小到大排好队,然后调用reverse()方法原地翻转就可以啦。
什么?太麻烦?好吧,大家真是越来越懒了……很好,“懒”有时候确实是发明创新的原动力。其实,sort()这个方法有三个参数,语法形式为:
sort(func, key, reverse)
func和key参数用于设置排序的算法和关键字,默认是使用归并排序,算法问题不在这里讨论,感兴趣的朋友可以参考小甲鱼的另一部视频教程——《数据结构和算法》。这里讨论sort()方法的第三个参数:reverse,没错,就是刚刚学的那个reverse()方法的reverse。不过这里作为sort()的一个默认参数,它的默认值是sort(reverse=False),表示不颠倒顺序。因此,只需要把False改为True,列表就相当于从大到小排序:
>>> list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0] >>> list1.sort(reverse=True) >>> list1 [10, 9, 8, 6, 5, 3, 2, 1, 0]