Python 编程/序列
序列允许您以一种组织且高效的方式存储多个值。共有七种序列类型:字符串、字节、列表、元组、字节数组、缓冲区和范围对象。字典和集合是用于存储顺序数据的容器。
我们已经介绍过字符串,但那是在您不知道什么是序列之前。在其他语言中,数组中的元素以及有时字符串中的字符可以使用方括号或下标运算符进行访问。这在 Python 中也适用。
>>> "Hello, world!"[0]
'H'
>>> "Hello, world!"[1]
'e'
>>> "Hello, world!"[2]
'l'
>>> "Hello, world!"[3]
'l'
>>> "Hello, world!"[4]
'o'
索引从 0 到 n-1 编号,其中 n 是项目(或字符)的数量,它们从字符串的开头开始赋予字符。
H e l l o , w o r l d ! 0 1 2 3 4 5 6 7 8 9 10 11 12
负索引(从 -1 到 -n 编号)从字符串的末尾开始计数。
H e l l o , w o r l d ! -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
>>> "Hello, world!"[-2]
'd'
>>> "Hello, world!"[-9]
'o'
>>> "Hello, world!"[-13]
'H'
>>> "Hello, world!"[-1]
'!'
如果括号中给出的数字小于 -n 或大于 n-1,则我们会得到一个 IndexError 错误。
但在 Python 中,冒号 : 允许方括号取两个数字。对于任何仅使用数字索引的序列,这将返回指定索引之间的部分。这称为“切片”,切片字符串的结果通常称为“子字符串”。
>>> "Hello, world!"[3:9]
'lo, wo'
>>> string = "Hello, world!"
>>> string[:5]
'Hello'
>>> string[-6:-1]
'world'
>>> string[-9:]
'o, world!'
>>> string[:-8]
'Hello'
>>> string[:]
'Hello, world!'
如上所示,如果省略第一个和第二个数字,则它们将取其默认值,分别为 0 和 n-1,分别对应于序列的开头和结尾(在本例中)。还要注意,括号在左侧是包含的,但在右侧是排除的:在上面第一个例子 [3:9] 中,索引 3 处的字符 'l' 包含在内,而索引 9 处的字符 'r' 被排除在外。
我们可以在括号中添加第三个数字,通过在括号中添加一个冒号,这表示切片的增量步长。
>>> s = "Hello, world!"
>>> s[3:9:2] #returns the substring s[3]s[5]s[7]
'l,w'
>>> s[3:6:3] #returns the substring s[3]
'l'
>>> s[:5:2] #returns the substring s[0]s[2]s[4]
'Hlo'
>>> s[::-1] #returns the reverted string
'!dlrow ,olleH'
>>> s[::-2] #returns the substring s[-1]s[-3]s[-5]s[-7]s[-9]s[-11]s[-13]
'!lo olH'
增量步长可以是正数或负数。对于正增量步长,切片是从左到右的,对于负增量步长,切片是从右到左的。此外,我们应该意识到
- 增量步长的默认值为 1
- 当增量步长为负时,第一个和第二个数字的默认值分别变为 -1(或 n)和 -n(或 0)(当增量步长为正时,与上面的默认值相同)
- 增量步长不能为 0
列表正如其名:一个按顺序组织的值列表。列表使用方括号创建。例如,空列表将这样初始化
spam = []
列表的值用逗号分隔。例如
spam = ["bacon", "eggs", 42]
列表可以包含不同类型的对象。它可以同时保存字符串“eggs”和“bacon”,以及数字 42。
与字符串中的字符类似,列表中的项目可以通过从 0 开始的索引进行访问。要访问列表中的特定项目,您可以通过列表的名称,后跟括号内列表中项目的编号来引用它。例如
>>> spam
['bacon', 'eggs', 42]
>>> spam[0]
'bacon'
>>> spam[1]
'eggs'
>>> spam[2]
42
您也可以使用负数,它从列表的末尾向后计数。
>>> spam[-1]
42
>>> spam[-2]
'eggs'
>>> spam[-3]
'bacon'
len()
函数也适用于列表,返回数组中的项目数量。
>>> len(spam)
3
请注意,len()
函数计算列表内部项目的数量,因此 spam 中的最后一个项目(42)具有索引 (len(spam) - 1)。
列表中的项目也可以更改,就像普通变量的内容一样。
>>> spam = ["bacon", "eggs", 42]
>>> spam
['bacon', 'eggs', 42]
>>> spam[1]
'eggs'
>>> spam[1] = "ketchup"
>>> spam
['bacon', 'ketchup', 42]
(字符串由于不可变,因此无法修改。)与字符串一样,列表也可以被切片。
>>> spam[1:]
['eggs', 42]
>>> spam[:-1]
['bacon', 'eggs']
也可以向列表中添加项目。有许多方法可以做到这一点,最简单的方法是使用列表的 append() 方法。
>>> spam.append(10)
>>> spam
['bacon', 'eggs', 42, 10]
请注意,您不能通过指定超出其范围的索引来手动插入元素。以下代码将失败。
>>> spam[4] = 10
IndexError: list assignment index out of range
相反,您必须使用 insert() 函数。如果要在列表中的某个索引处插入一个项目,可以使用列表的 insert() 方法,例如
>>> spam.insert(1, 'and')
>>> spam
['bacon', 'and', 'eggs', 42, 10]
您还可以使用 del
语句从列表中删除项目。
>>> spam
['bacon', 'and', 'eggs', 42, 10]
>>> del spam[1]
>>> spam
['bacon', 'eggs', 42, 10]
>>> spam[0]
'bacon'
>>> spam[1]
'eggs'
>>> spam[2]
42
>>> spam[3]
10
如您所见,列表会重新排序自身,因此编号中没有间隙。
列表有一个不寻常的特性。给定两个列表 a 和 b,如果您将 b 设置为 a,并更改 a,b 也会被更改。
>>> a=[2, 3, 4, 5]
>>> b=a
>>> del a[3]
>>> print(a)
[2, 3, 4]
>>> print(b)
[2, 3, 4]
这可以通过使用 b=a[:]
来轻松解决。
有关列表的更多说明,或了解如何创建二维数组,请参阅 数据结构/列表
元组类似于列表,但它们是不可变的。一旦设置了元组,就无法以任何方式更改它:您无法添加、更改或删除元组的元素。否则,元组的工作方式与列表相同。
要声明元组,您使用逗号。
unchanging = "rocks", 0, "the universe"
通常需要使用括号来区分不同的元组,例如在同一行上进行多个赋值时。
foo, bar = "rocks", 0, "the universe" # 3 elements here fail - too many values
foo, bar = "rocks", (0, "the universe") # 2 elements here because the second element is a tuple
可以使用不必要的括号,不会造成损害,但嵌套括号表示嵌套元组。
>>> var = "me", "you", "us", "them"
>>> var = ("me", "you", "us", "them")
两者都生成
>>> print(var)
('me', 'you', 'us', 'them')
但
>>> var = ("me", "you", ("us", "them"))
>>> print(var)
('me', 'you', ('us', 'them')) # A tuple of 3 elements, the last of which is itself a tuple.
有关元组的更多说明,请参阅 数据结构/元组
字典也类似于列表,并且它们是可变的——您可以添加、更改和删除字典中的元素。但是,字典中的元素不像列表那样绑定到数字。字典中的每个元素都有两个部分:键和值。调用字典的键将返回与该键关联的值。您可以将列表视为一种特殊的字典,其中每个元素的键都是一个按数字顺序排列的数字。
字典使用花括号声明,每个元素首先由其键声明,然后是一个冒号,然后是其值。例如
>>> definitions = {"guava": "a tropical fruit", "python": "a programming language", "the answer": 42}
>>> definitions
{'python': 'a programming language', 'the answer': 42, 'guava': 'a tropical fruit'}
>>> definitions["the answer"]
42
>>> definitions["guava"]
'a tropical fruit'
>>> len(definitions)
3
此外,向字典中添加元素要简单得多:只需像声明变量一样声明它即可。
>>> definitions["new key"] = "new value"
>>> definitions
{'python': 'a programming language', 'the answer': 42, 'guava': 'a tropical fruit', 'new key': 'new value'}
有关字典的更多说明,请参阅 数据结构/字典
集合就像列表一样,除了它们是无序的并且不允许重复值。集合的元素既不绑定到数字(如列表和元组),也不绑定到键(如字典)。使用集合而不是其他数据类型的原因是,对于大量项目,集合比列表或元组快得多,并且集合提供快速的数据插入、删除和成员资格测试。集合还支持数学集合运算,例如测试子集和查找两个集合的并集或交集。
>>> mind = set([42, 'a string', (23, 4)]) #equivalently, we can use {42, 'a string', (23, 4)}
>>> mind
set([(23, 4), 42, 'a string'])
>>> mind = set([42, 'a string', 40, 41])
>>> mind
set([40, 41, 42, 'a string'])
>>> mind = set([42, 'a string', 40, 0])
>>> mind
set([40, 0, 42, 'a string'])
>>> mind.add('hello')
>>> mind
set([40, 0, 42, 'a string', 'hello'])
请注意,集合是无序的,您添加到集合中的项目最终将位于不确定的位置,并且它也可能随时更改。
>>> mind.add('duplicate value')
>>> mind.add('duplicate value')
>>> mind
set([0, 'a string', 40, 42, 'hello', 'duplicate value'])
集合不能包含单个值多次。与可以包含任何内容的列表不同,可以包含在集合中的数据类型受到限制。集合只能包含可散列的、不可变的数据类型。整数、字符串和元组是可散列的;列表、字典和其他集合(除了冻结集,见下文)不是。
冻结集和集合之间的关系就像元组和列表之间的关系。冻结集是集合的不可变版本。一个例子
>>> frozen=frozenset(['life','universe','everything'])
>>> frozen
frozenset(['universe', 'life', 'everything'])
Python 还具有其他类型的序列,尽管这些序列的使用频率较低,并且需要在使用前从标准库中导入。我们这里只简单介绍一下。
- 数组
- 一个类型化的列表,数组只能包含同构的值。
- collections.defaultdict
- 一个字典,当找不到元素时,返回默认值而不是错误。
- collections.deque
- 一个双端队列,允许快速操作队列的两端。
- heapq
- 一个优先级队列。
- 队列
- 一个线程安全的、多生产者、多消费者的队列,用于多线程程序。请注意,列表也可以在单线程代码中用作队列。
有关集合的更多说明,请参阅 数据结构/集合
Python 中一些有用的数据类型并不包含在标准库中。其中一些在使用上非常专业化。我们将提及一些更广为人知的第三方类型。
- numpy.array
- 用于大量数值计算 => 请参阅numpy 部分
- sorteddict
- 顾名思义,一个排序字典
- 编写一个程序,将 5、10 和 "twenty" 放入列表中。然后从列表中删除 10。
- 编写一个程序,将 5、10 和 "twenty" 放入元组中。
- 编写一个程序,将 5、10 和 "twenty" 放入集合中。将 "twenty"、10 和 5 放入另一个集合中,故意以不同的顺序。打印出这两个集合并注意它们的排序。
- 编写一个程序,构造一个元组,其中一个元素是冻结集。
- 编写一个程序,创建一个字典,将 1 映射到 "Monday",2 映射到 "Tuesday",等等。
- 序列类型 — 列表、元组、范围 在 Python 库参考中,docs.python.org