零基础入门学习Python(第2版)
上QQ阅读APP看书,第一时间看更新

第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]