首页 > 代码库 > 列表、元组
列表、元组
Python可以在内存中存储信息,有时候,我们想可以把一堆东西存储在一起,放在某种“组”或者“集合”中,这样一来,就可以一次对整个集合做某些处理,也能更容易地记录一组东西。Python把这类集合叫做列表(list)。
列表非常有用,很多很多程序里都用到了列表。后面几章开始讨论图形和游戏编程时我们的例子中就会大量使用列表,因为游戏中的很多图形对象通常都存储在列表中。
12.1 什么是列表
列表是是一组有顺序的元素的集合。每个元素可以是数字、字符串。比如,让你建一个家庭成员列表,在Python中,就要写成:
family = [‘Mom‘, ‘Dad‘, ‘Junior‘, ‘Baby‘]
如果我让你写下你的幸运数字,在Python中,就要写成:
luckyNumbers = [2, 7, 14, 26, 30]
family和luckyNumbers都是Python列表的例子,列表中的单个元素就叫做项或者元素(item)。可以看到,Python中的列表与你在日常生活中建立的列表并没有太大差异。列表使用中括号来指出从哪里开始,到哪里结束,另外用逗号分隔列表内的各项。
12.2 创建列表
family和luckyNumbers都是变量。前面曾经说过,可以为变量赋不同类型的值。我们已经为变量赋过数和字符串,还可以为变量赋一个列表。
就像创建任何其他变量一样,创建列表也是要为它赋某个值,如前面对luckyNumbers的赋值。另外还可以创建一个空的列表,如下:
newList = []
中括号没有任何元素,所以这个列表是空的。不过一个空列表有什么用呢?为什么想要创建这样一个空列表呢?
嗯,很多情况下,我们无法提前知道列表中会有些什么。我们不知道其中会有多少元素,也不知道这些元素是什么,只知道将会用一个列表来保存这些内容。有了空列表后,程序就可以向这个列表中增加元素。这又怎么做到呢?
12.3 向列表增加元素
要向列表增加元素,需要使用append()。在交互模式中试试下面的代码:
你会得到这样的结果:
[‘David‘]
再来增加一个元素:
>>> friends.append(‘Mary‘)
>>> print friends
[‘David‘, ‘Mary‘]
记住,向列表增加元素之前,必须先创建列表(可以是空列表,也可以非空)。这就像在做一个蛋糕:不能直接把各种配料倒在一起,而是先将配料倒入碗中,不然肯定会弄得到处都是!
12.4 这个点是什么
为什么要在friends和append()之间加一个点(.)呢?嗯,现在要谈到一个重要的话题了:这就是对象。我们会在第14章学习关于对象的更多内容,不过现在先简单解释一下。
术语箱
追加(append)是把一个东西加在最后面。把一个东西追加到列表时,会把它增加到列表的末尾。
Python中的很多东西都是对象(object)。要想用对象做某种处理,需要这个对象的名字(变量名),然后是一个点,再后面是要对对象做的操作。所以要向friends列表追加一个元素,就要写成:
friends.append(something)
这个点有类似于“的”,我的…,她的…
12.5 列表可以包含任何内容
列表可以包含Python能存储的任何类型的数据,这包括数字、字符串、对象,甚至可以包含其他列表。并不要求列表中的元素是同种类型或同一种东西。这说明,一个列表中可以同时包含不同类型,例如数字和字符串,可能像这样:
my_list = [5, 10, 23.76, ‘Hello‘, myTeacher, 7, another_list]
面用一些简单的内容建立一个新列表,比如字母表中的字母,这样我们在学习列表时就能更容易地了解做了些什么。在交互模式中键入下面的代码:
>>> letters = [‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘]
12.6 从列表获取元素
可以按元素的索引(index)号从列表获取单个元素。列表索引从0开始,所以这个列表中的第一项就是letters[0]。
>>> print letters[0]
a
再来试一个:
>>> print letters[3]
d
12.7 列表“分片”
列表的元素不必是同一类型。
>>> a = [‘spam‘, ‘eggs‘, 100, 1234]
>>> a
[‘spam‘, ‘eggs‘, 100, 1234]
就像字符串索引,列表从 0 开始检索。列表可以被切片和连接:
>>> a[0]
‘spam‘
>>> a[3]
1234
>>> a[-2]
100
>>> a[1:-1]
[‘eggs‘, 100]
>>> a[:2] + [‘bacon‘, 2*2]
[‘spam‘, ‘eggs‘, ‘bacon‘, 4]
>>> 3*a[:3] + [‘Boo!‘]
[‘spam‘, ‘eggs‘, 100, ‘spam‘, ‘eggs‘, 100, ‘spam‘, ‘eggs‘, 100, ‘Boo!‘]
切片详解
为什么需要切片?
取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下:
>>> L = [‘Michael‘, ‘Sarah‘, ‘Tracy‘, ‘Bob‘, ‘Jack‘]
取前3个元素,应该怎么做?
笨办法:
>>> [L[0], L[1], L[2]]
[‘Michael‘, ‘Sarah‘, ‘Tracy‘]
之所以是笨办法是因为扩展一下,取前N个元素就没辙了。
取前N个元素,也就是索引为0-(N-1)的元素,可以用循环:
>>> r = []
>>> n = 3
>>> for i in range(n):
... r.append(L[i])
...
>>> r
[‘Michael‘, ‘Sarah‘, ‘Tracy‘]
对这种经常取指定索引范围的操作,用循环十分繁琐,因此,Python提供了切片(Slice)操作符,能大大简化这种操作。
对应上面的问题,取前3个元素,用一行代码就可以完成切片:
>>> L[0:3]
[‘Michael‘, ‘Sarah‘, ‘Tracy‘]
L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。
如果第一个索引是0,还可以省略:
>>> L[:3]
[‘Michael‘, ‘Sarah‘, ‘Tracy‘]
也可以从索引1开始,取出2个元素出来:
>>> L[1:3]
[‘Sarah‘, ‘Tracy‘]
类似的,既然Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片,试试:
>>> L[-2:]
[‘Bob‘, ‘Jack‘]
>>> L[-2:-1]
[‘Bob‘]
记住倒数第一个元素的索引是-1。
切片操作十分有用。我们先创建一个0-99的数列:
>>> L = range(100)
>>> L
[0, 1, 2, 3, ..., 99]
可以通过切片轻松取出某一段数列。比如前10个数:
>>> L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
后10个数:
>>> L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
前11-20个数:
>>> L[10:20]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
前10个数,每两个取一个:
>>> L[:10:2]
[0, 2, 4, 6, 8]
所有数,每5个取一个:
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
所有的切片操作都会返回新的列表,包含求得的元素。这意味着以下的切片操作返回列表 a 的一个浅拷贝的副本:
>>> a[:]
[‘spam‘, ‘eggs‘, 100, 1234]
不像 不可变的 字符串,列表允许修改元素
>>> a
[‘spam‘, ‘eggs‘, 100, 1234]
>>> a[2] = a[2] + 23
>>> a
[‘spam‘, ‘eggs‘, 123, 1234]
关于列表更多的内容
Python 的列表数据类型包含更多的方法。 这里是所有的列表对象方法:
list.append(x)
把一个元素添加到链表的结尾,相当于 a[len(a):] = [x] 。
list.extend(L)
将一个给定列表中的所有元素都添加到另一个列表中,相当于 a[len(a):] = L 。
list.insert(i, x)
在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个链表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。
list.remove(x)
删除链表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。
list.pop([i])
从链表的指定位置删除元素,并将其返回。如果没有指定索引, a.pop() 返回最后一个元素。元素随即从链表中被删除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
list.index(x)
返回链表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
list.count(x)
返回 x 在链表中出现的次数。
list.sort()
对链表中的元素就地进行排序。
list.reverse()
就地倒排链表中的元素。
下面这个示例演示了链表的大部分方法:
>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.25), a.count(‘x‘)
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
也许大家会发现像 insert, remove 或者 sort 这些修改列表的方法没有打印返回值–它们返回 None 。 在 python 中对所有可变的数据类型这是统一的设计原则。
12.8 可改变和不可改变
如果还记得第2章中的内容,我们说过,真正改变一个数或字符串是做不到的,你能改变的只是把一个名字指派到哪个数或字符串(换句话说,你只能移动标签)。不过,Python中确实有一些可以改变的类型,列表就是其中之一。刚才已经看到,列表可以追加或删除元素,另外列表中的元素还可以排序或逆置。
这两种不同的变量分别称为可改变和不可改变的变量。可改变(mutable)是指“能够改变”或者“可以改变”。不可改变(immutable)表示“不能改变”或者“不可以改变”。在Python中,数字和字符串是不可改变的(不能改变),而列表是可改变的(能够改变)。
元组——不可改变的列表
有些情况下你可能不希望列表可以改变。Python中有没有一种不可改变的列表呢?答案是肯定的。确实有一个名为元组(tuple)的类型,这就属于不可改变的列表。可以这样来建立元组:
my_tuple = ("red", "green", "blue")
这里使用了圆括号,而不是列表使用的中括号。
由于元组是不可改变的,所以不能对元组完成排序,也不能追加和删除元素。一旦用一组元素创建一个元组,它就会一直保持不变。
1. 元组和序列
我们知道链表和字符串有很多通用的属性,例如索引和切割操作。它们是 序列 类型(参见 typesseq )中的两种。因为 Python 是一个在不断进化的语言,也可能会加入其它的序列类型,这里介绍另一种标准序列类型: 元组 tuple。
一个元组由数个逗号分隔的值组成,例如:
>>> t = 12345, 54321, ‘hello!‘
>>> t[0]
12345
>>> t
(12345, 54321, ‘hello!‘)
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, ‘hello!‘), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ‘tuple‘ object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
如你所见,元组在输出时总是有括号的,以便于正确表达嵌套结构。在输入时可以有或没有括号,不过经常括号都是必须的(如果元组是一个更大的表达式的一部分)。不能给元组的一个独立的元素赋值(尽管你可以通过联接和切割来模拟)。还可以创建包含可变对象的元组,例如链表。
虽然元组和列表很类似,它们经常被用来在不同的情况和不同的用途。元组有很多用途。例如 (x, y) 坐标对,数据库中的员工记录等等。元组就像字符串,不可改变。
一个特殊的问题是构造包含零个或一个元素的元组:为了适应这种情况,语法上有一些额外的改变。一对空的括号可以创建空元组;要创建一个单元素元组可以在值后面跟一个逗号(在括号中放入一个单值不够明确)。丑陋,但是有效。例如:
>>> empty = ()
>>> singleton = ‘hello‘, # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
(‘hello‘,)
语句 t = 12345, 54321, ‘hello!‘ 是 元组封装 (tuple packing)的一个例子:值 12345 , 54321 和 ‘hello!‘ 被封装进元组。其逆操作可能是这样:
>>> x, y, z = t
这个调用等号右边可以是任何线性序列,称之为 序列拆封 非常恰当。序列拆封要求左侧的变量数目与序列的元素个数相同。要注意的是可变参数(multiple assignment )其实只是元组封装和序列拆封的一个结合。
2. 集合
Python 还包含了一个数据类型 set (集合) 。集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 sysmmetric difference(对称差集)等数学运算。
大括号或 set() 函数可以用来创建集合。 注意:想要创建空集合,你必须使用 set() 而不是 {} 。后者用于创建空字典,我们在下一节中介绍的一种数据结构。
以下是一个简单的演示:
>>> basket = [‘apple‘, ‘orange‘, ‘apple‘, ‘pear‘, ‘orange‘, ‘banana‘]
>>> fruit = set(basket) # create a set without duplicates
>>> fruit
set([‘orange‘, ‘pear‘, ‘apple‘, ‘banana‘])
>>> ‘orange‘ in fruit # fast membership testing
True
>>> ‘crabgrass‘ in fruit
False
>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set(‘abracadabra‘)
>>> b = set(‘alacazam‘)
>>> a # unique letters in a
set([‘a‘, ‘r‘, ‘b‘, ‘c‘, ‘d‘])
>>> a - b # letters in a but not in b
set([‘r‘, ‘d‘, ‘b‘])
>>> a | b # letters in either a or b
set([‘a‘, ‘c‘, ‘r‘, ‘d‘, ‘b‘, ‘m‘, ‘z‘, ‘l‘])
>>> a & b # letters in both a and b
set([‘a‘, ‘c‘])
>>> a ^ b # letters in a or b but not both
set([‘r‘, ‘d‘, ‘b‘, ‘m‘, ‘z‘, ‘l‘])
类似 for lists ,这里有一种集合推导式语法:
>>> a = {x for x in ‘abracadabra‘ if x not in ‘abc‘}
>>> a
{‘r‘, ‘d‘}
列表、元组