列表和元组
新的概念:数据结构。 数据结构是以某种方式(如通过编号)组合起来的数据元素(如数、字符乃至其他数据结构)集合。
Python中最基本的数据结构是 序列(sequence)。
序列中的每个元素都有编号,即其位置或索引,其中第一个元素的索引位0,第二个元素的索引位1,依次类推。
序列概述
最常用的两种序列: 列表和元组。 另一种重要的序列是字符串。
列表和元组的主要不同在于,列表是可以修改的,而元组不可以。 列表适用于需要中途添加元素的情形,而元组适用于处于某种考虑需要禁止修改序列的情形。
几乎所有情况下都可使用列表来代替元组,一种情况例外:将元组用作字典键。
在需要处理一系列值时,序列很有用。
例:表示人,第一个元素为姓名,第二个元素为年龄。
>>> edward = ['Edward Gumby', 42]
序列还可包含其他序列,因此可创建一个由数据库中所有人员组成的列表:
>>> lilei = ['Lilei', 18]
>>> hanmeimei = ['Hanmeimei ', 17]
>>> database = [edward, john]
>>> database
[['Lilei', 18], ['Hanmeimei ', 17]]
注意:
Python支持一种数据结构的基本概念,名为容器(container)。
容器基本上就是可包含其他对象的对象。
两种主要的容器是序列(如列表和元组)和映射(如字典)。
在序列中每个元素都有编号,而在映射中,每个元素都有名称(也叫键)。
有一种既不是序列也不是映射的容器,它就是集合(set)。
通用的序列操作
适用于所有序列的操作: 索引、切片、相加、相乘 和 成员资格检查。
还有一个重要的操作:迭代(iteration)。
索引
序列中的所有元素都有编号——从0开始递增。 可使用编号访问各个元素:
>>> greeting = 'hello'
>>> greeting[0]
'h'
注意,字符串就是由字符组成的序列。
Python没有专门用于表示字符的类型,因此一个字符就是只包含一个元素的字符串。
上述访问方式称为 索引(indexing)。 可使用索引获取元素,这种方式适用于所有序列。 使用负数索引时,将从右(即最后一个元素)开始往左数,因此-1是最后一个元素的位置。
>>> greeting[-1]
'o'
如果函数调用返回一个序列,可直接对其执行索引操作。
>>> fourth = input('Year:' )[3]
Year:2005
>>> fourth
'5'
切片
可使用 切片(slicing)来访问特定范围内的元素。
可使用两个索引,并用冒号分隔:
>>> tag = '<a href="http://www.python.org">Python web site</a>'
>>> tag[9:30]
'http://www.python.org'
>>> tag[32:-4]
'Python web site'
切片适用于提取序列的一部分,其中的编号非常重要: 第一个索引是包含的第一个元素的编号,但第二个索引时切片后余下的第一个元素的编号。 如:
>>> number = [1,2,3,4,5,6,7,8,9,10]
>>> number[3:6]
[4, 5, 6]
>>> number[0:1]
[1]
即,提供两个索引来指定切片的边界,其中第一个索引指定的元素包含在切片内,但第二个作用指定的元素不包含在切片内。
1. 绝妙的简写
访问上述数字列表的最后三个元素,可以写成:
>>> number[7:10]
[8, 9, 10]
在这里索引10指的是第11个元素:它不存在,但确实是到达最后一个元素后再前进一步所处的位置。
从列表末尾开始数,可使用负数索引:
>>> number[-3:-1]
[8, 9]
使用索引0,即到达列表末尾后再前进一步所处的位置:
>>> number[-3:0]
[]
执行切片操作时,如果第一个索引指定的元素位于第二个索引指定的元素后面,结果就为空序列。
简写: 如果切片结束与序列末尾,可省略第二个索引。
>>> number[-3:]
[8, 9, 10]
如果切片始于序列开头,可省略第一个索引。
>>> number[:3]
[1, 2, 3]
复制整个序列,可将两个索引都省略。
>>> number[:]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2. 更大的步长
执行委派操作时,显示或隐式地指定起点和终点,但通常省略另一个参数,即步长。
在普通切片中,步长为1。意味着从一个元素移到下一个元素。
>>> number[0:10:1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
这就是显示地指定了步长。 如果指定的步长大于1,将跳过一些元素。 例:
>>> number[0:10:3]
[1, 4, 7, 10]
>>> number[3:6:3]
[4]
显示地指定步长时,也可使用上述的简写方式。
>>> number[::4]
[1, 5, 9]
步长不能为0,否则无法向前移动,但可以为负数,即从右向左提取元素。
>>> number[8:3:-1]
[9, 8, 7, 6, 5]
>>> number[10:0:-2]
[10, 8, 6, 4, 2]
>>> number[0:10:-2]
[]
>>> number[::-2]
[10, 8, 6, 4, 2]
>>> number[5::-2]
[6, 4, 2]
>>> number[:5:-2]
[10, 8]
步长为负数时,第一个索引必须比第二个索引大。
步长为整数,它从起点移到终点,而步长为负数时,它从终点移动起点。
序列相加
可使用加法运算符来拼接序列。
>>> [1,2,3]+[4,5,6]
[1, 2, 3, 4, 5, 6]
>>> 'Hello, '+'world!'
'Hello, world!'
>>> [1,2,3]+'world!'
Traceback (most recent call last):
File "<pyshell#144>", line 1, in <module>
[1,2,3]+'world!'
TypeError: can only concatenate list (not "str") to list
不能拼接列表和字符串。 一般而言,不能拼接不同类型的序列。
乘法
将序列与数 x 相乘时,将重复这个序列 x 次来创建一个新序列:
>>> 'python'*5
'pythonpythonpythonpythonpython'
>>> [42]*10
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
None、空列表和初始化
空列表是使用不包含任何内容的两个方括号( [ ] )表示的。 使用None表示“什么都没有”的值,如表示还没有在列表中添加任何内容。
将列表的长度初始化为10,可如下操作:
>>> sequence = [None]*10
>>> sequence
[None, None, None, None, None, None, None, None, None, None]
成员资格
要检查特定的值是否包含在序列中,可使用运算符 in。 它检查是否满足指定的条件,并返回相应的值:满足时返回True,不满足时返回False。 这样的运算符称为布尔运算符,而前述真值称为布尔值。
in 运算符的使用示例:
>>> permissions = 'rw'
>>> 'w' in permissions
True
>>> 'x' in permissions
False
>>> users = ['mlh','foo','bar']
>>> input('Enter your user name: ') in users
Enter your user name: mlh
True
>>> subject = '$$$ Get rich now!!! $$$'
>>> '$$$' in subject
True
注意: 一般而言,运算符 in 检查指定的对象是否是序列(或其他集合)的成员(即其中的一个元素),但对字符串来说,只有它包含的字符才是其成员或元素。 如下代码合理:
>>> 'P' in 'Python'
True
长度、最小值和最大值
内置函数 len、min 和 max 很有用。 函数 len 返回序列包含的元素个数, min 和 max 分别返回序列中最小和最大的元素。
>>> numbers = [100,34,678]
>>> len(numbers)
3
>>> max(numbers)
678
>>> min(numbers)
34
>>> max(2,3)
3
>>> min(9,3,2,5)
2
最后两个表达式中,调用max和min时指定的实参并不是序列,而是直接将数作为实参。
列表:Python的主力
列表是可变的,即可修改其内容。 列表有很多特有的方法。
函数 list
使用字符串创建列表,可使用函数 list。
>>> list('Helllo')
['H', 'e', 'l', 'l', 'l', 'o']
注意,可将任何序列(而不仅仅是字符串)作为list的参数。
基本的列表操作
可对列表执行所有标准序列操作,如索引、切片、拼接和相乘,但列表是可以修改的。
修改列表的方式:给元素赋值、删除元素、给切片赋值以及使用列表的方法。
1. 修改列表:给元素赋值
使用索引表示法给特定位置的元素赋值,如 x[1] = 2。
>>> x=[1,1,1]
>>> x[1]=2
>>> x
[1, 2, 1]
注意,不能给不存在的元素赋值。
2. 删除元素
使用 del 语句,从列表中删除元素:
>>> names = ['Alice','Beth','Cecil','Dee-Dee','Earl']
>>> del names[2]
>>> names
['Alice', 'Beth', 'Dee-Dee', 'Earl']
3. 给切片赋值 切片是一项极其强大的功能,而能够给切片赋值让这项功能显得更加强大。
>>> name = list('Perl')
>>> name
['P', 'e', 'r', 'l']
>>> name[2:] = list('ar')
>>> name
['P', 'e', 'a', 'r']
可同时给多个元素赋值。
通过使用切片赋值,可将切片替换为长度与其不同的序列。
>>> name = list('Perl')
>>> name[1:] = list('ython')
>>> name
['P', 'y', 't', 'h', 'o', 'n']
还可在不替换原有元素的情况下插入新元素。
>>> numbers = [1,5]
>>> numbers[1:1] = [2,3,4]
>>> numbers
[1, 2, 3, 4, 5]
这里,“替换”了一个空切片,相当于插入了一个序列。 可采用相反的措施来删除切片。
>>> numbers
[1, 2, 3, 4, 5]
>>> numbers[1:4]=[]
>>> numbers
[1, 5]
列表方法
方法是与对象(列表、数、字符串等)联系紧密的函数。 通常,如下方法调用:
o
b
j
e
c
t
.
m
e
t
h
o
d
(
a
r
g
u
m
e
n
t
s
)
object.method(arguments)
object.method(arguments)
列表包含多个用来查看和修改其内容的方法:
1. append
方法 append 用于将一个对象附加到列表末尾。
>>> lst = [1,2,3]
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
append 就地修改类别。 意味着它不会返回修改后的新列表,而是之间修改旧列表。
2. clear
方法 clear 就地清空列表的内容。
>>> lst = [1,2,3]
>>> lst.clear()
>>> lst
[]
类似于切片赋值语句 lst[:] = [ ] 。
3. copy
方法 copy 复制列表。 常规复制只是将另一个名称关系到列表。
>>> a = [1,2,3]
>>> b = a
>>> b[1] = 4
>>> a
[1, 4, 3]
要让a和b指向不同的列表,就必须将b关联到a的副本。
>>> a = [1,2,3]
>>> b = a.copy()
>>> b[1] = 4
>>> a
[1, 2, 3]
类似于使用a[:] 或 list(a),它们也都复制a。
4. count
方法 count 计算指定的元素在列表中出现了多少次。
>>> ['to','be','or','not','to','be',].count('to')
2
>>> x = [[1,2],1,1,[2,1,[1,2]]]
>>> x.count(1)
2
>>> x.count([1,2])
1
5. extend
方法 extend 能够同时将多个值附加到列表末尾,为此可将这些值组成的序列作为参数提供个方法 extend。 换言之,可使用一个列表来扩展另一个列表。
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6]
看起来类似于拼接,但存在一个重要差别,就是将修改被扩展的序列(这里是 a)。 在常规拼接中,情况是返回一个全新的序列。
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a + b
[1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3]
拼接的结果与上述一致,但这里a并没有被修改。
拼接操作并非就地执行的,即它不会修改原来的列表。 要获得与extend相同的效果,可将列表赋给切片,如下所示:
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a[len(a):] = b
>>> a
[1, 2, 3, 4, 5, 6]
但这种方式,可读性不是很高。
6. index
方法 index 在列表中查找指定值第一次出现的索引。
>>> knights = ['We','are','the','knights','who','say','ni']
>>> knights.index('who')
4
>>> knights.index('her')
Traceback (most recent call last):
File "<pyshell#217>", line 1, in <module>
knights.index('her')
ValueError: 'her' is not in list
搜索单词 ‘who’ 时,发现它位于索引4处。
>>> knights[4]
'who'
搜索 ‘her’ 时引发了异常,因为根本就没有找到这个单词。
7. insert
方法 insert 用于将一个对象插入列表。
>>> numbers = [1,2,3,5,6,7]
>>> numbers.insert(3,'four')
>>> numbers
[1, 2, 3, 'four', 5, 6, 7]
也可使用切片赋值来获得与之一样的效果。
>>> numbers = [1,2,3,5,6,7]
>>> numbers[3:3] = ['four']
>>> numbers
[1, 2, 3, 'four', 5, 6, 7]
但可读性低。
8. pop
方法 pop 从列表中删除一个元素(末尾为最后一个元素),并返回这一元素。
>>> x = [1,2,3]
>>> x.pop()
3
>>> x
[1, 2]
>>> x.pop(0)
1
>>> x
[2]
注意,pop是唯一既修改列表又返回一个非None值的列表方法。
使用 pop 可实现一种常见的数据结构——栈(stack)。后进先出(LIFO)。
push和pop是普遍接受的两种栈操作(加入和取走)的名称。 Python没有提供push,但可以使用 append 来替代。
>>> x = [1,2,3]
>>> x.append(x.pop())
>>> x
[1, 2, 3]
提示:
要创建先进先出(FIFO)的队列,可使用 insert(0,...)代替append。
也可继续使用append,但用pop(0)替代pop()。
更佳的方案:使用模块 collections中的deque。
9. remove
方法 remove 用于删除第一个为指定值的元素。
>>> x = ['to','be','or','not','to','be']
>>> x.remove('be')
>>> x
['to', 'or', 'not', 'to', 'be']
>>> x.remove('bee')
Traceback (most recent call last):
File "<pyshell#236>", line 1, in <module>
x.remove('bee')
ValueError: list.remove(x): x not in list
remove是就地修改且不返回值的方法之一。不同于pop的是,它修改列表,但不返回任何值。
10. reverse
方法 reverse 按相反的顺序排序列列表中的元素。
>>> x = [1,2,3]
>>> x.reverse()
>>> x
[3, 2, 1]
reverse 修改列表,但不返回任何值。
11. sort
方法 sort 用于对列表就地排序。 意味着对原来的列表进行修改,使其元素按顺序排列,而不是返回排序后的列表的副本。
>>> x = [4,6,2,1,7,9]
>>> x.sort()
>>> x
[1, 2, 4, 6, 7, 9]
一种直观(但错误)的方式是像下面这样:
>>> x = [4,6,2,1,7,9]
>>> y = x.sort()
>>> print(y)
None
正确的方式之一是,先将y关联到 x的副本,再对y进行排序,如下:
>>> x = [4,6,2,1,7,9]
>>> y = x.copy()
>>> y.sort()
>>> x
[4, 6, 2, 1, 7, 9]
>>> y
[1, 2, 4, 6, 7, 9]
为获取排序后的列表的副本,另一种方式是使用函数 sorted。
>>> x = [4,6,2,1,7,9]
>>> y = sorted(x)
>>> x
[4, 6, 2, 1, 7, 9]
>>> y
[1, 2, 4, 6, 7, 9]
实际上,这个函数可用于任何序列,但总是返回一个列表。
>>> sorted('Python')
['P', 'h', 'n', 'o', 't', 'y']
要将元素按相反的顺序排列,可先使用 sort (或 sorted),再调用方法 reverse,也可使用参数 reverse。
12. 高级排序
方法 sort 接受两个可选参数:key 和 reverse。 这两个参数通常是按名称指定的,称为关键字参数。
参数key类似于参数 cmp:将其设置为一个用于排序的函数。
根据长度对元素进行排序,可将参数key设置为函数len。
>>> x = ['aardvark','abalone','acme','add','aerate']
>>> x.sort(key=len)
>>> x
['add', 'acme', 'aerate', 'abalone', 'aardvark']
对于另一个关键字参数 reverse,只需将其指定为一个真情(True 或 False),以指出是否要按相反的顺序对列表进行排序。
>>> x = [4,6,2,1,7,9]
>>> x.sort(reverse=True)
>>> x
[9, 7, 6, 4, 2, 1]
函数sorted 也接受参数 key 和 reverse。
元组:不可修改的序列
与列表一样,元组也是序列,唯一的差别在于元组是不能修改的。 元组的语法很简单,只要将一些值用逗号分隔,就能自动创建一个元组。
>>> 1,2,3
(1, 2, 3)
元组还可用圆括号括起来。
>>> (1, 2, 3)
(1, 2, 3)
空元组用两个不包含任何内容的圆括号表示。
>>> ()
()
只包含一个值的元组,也必须在它后面加上逗号。
>>> 42
42
>>> 42,
(42,)
>>> (42,)
(42,)
最后两个示例创建的元组长度为1,而第一个示例根本没有创建元组。
逗号至关重要,仅将值用圆括号括起来不管用:(42)与42完全等效。 仅加上一个逗号,就能完全改变表达式的值。
>>> 3 * (40 + 2)
126
>>> 3 * (40 + 2,)
(42, 42, 42)
函数tuple的工作原理与 list很像:它将一个序列作为参数,并将其转换为元组。 如果参数已经是元组,就原封不动地返回它。
>>> tuple([1,2,3])
(1, 2, 3)
>>> tuple('abc')
('a', 'b', 'c')
>>> tuple((1,2,3))
(1, 2, 3)
元组的创建及其元素的访问方式与其他序列相同。
>>> x = 1,2,3
>>> x[1]
2
>>> x[0:2]
(1, 2)
元组的切片也是元组,就像列表的切片也是列表一样。
熟悉元组的原因有以下两个:
- 它们用作映射中的键(以及集合的成员),而列表不行。
- 有些内置函数和方法返回元组,这意味着必须跟它们打交道。
一般而言,使用列表足以满足对序列的需求。
小结
本章介绍的新函数
学习参考资料:
《Python基础教程》 第3版
|