https://docs.python.org/3/tutorial/index.html 摘抄 + 翻译 §5 主要涉及数据结构。

5. Data Structures

这一节回顾一些学过的,同时补充一点新知识。

5.1. More on Lists

List 数据类型还有更多方法。

  • list.append(x) 在末尾添加一个元素,相当于a[len(a):]=[x] 注意这里写成a[len(a)]=[x]a[len(a):]=x都是不对的,小心。

  • list.extend(iterable) 使用可迭代对象中的所有元素来扩展列表,相当于a[len(a):] = iterable

  • list.insert(i, x) 在给定的位置插入一个元素。第一个参数是要插入的元素的索引,所以a.insert(0, x)插入列表头部,a.insert(len(a), x)等同于a.append(x)

  • list.remove(x) 移除列表中第一个值为 x 的元素。如果没有这样的元素,则抛出 (raise) ValueError 异常。

  • list.pop([i]) 删除列表中给定位置的元素并返回它。如果没有给定位置参数,pop()将会删除并返回列表中的最后一个元素。 (方法签名中i两边的方括号表示这个参数是可选的,而不是要你输入方括号。在 Python 参考库中会经常看到这种表示方法)。

  • list.clear() 删除列表中所有的元素,相当于del a[:]

  • list.index(x[, start[, end]]) 返回列表中第一个值为 x 的元素的从零开始的索引,如果找不到将会抛出 ValueError 异常。 可选参数 start 和 end 用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是 start 参数。

  • list.count(x) 返回元素 x 在列表中出现的次数。

  • list.sort(*, key=None, reverse=False) 对列表中的元素进行排序。 两个可选参数(只能是关键字参数,这里用*表示这一规定,可以参考 ## 4.7.3):key 指定一个带有一个参数的函数,用于从 iterable 中的每个元素中提取比较键(例如,key=str.lower(大写转成小写)),默认值是 None(直接比较元素);reverse 是一个布尔值,表示倒序排列。 从 python2.2 开始,排序被保证为稳定的。意思是说多个元素如果有相同的 key,则排序前后他们的先后顺序不变。这有助于多次排序(例如,按部门排序,然后按工资级别排序)。 在 Py3.0 之前,其实用的是一个 cmp 参数,用于定义元素之间的比较方法(例如只关注某个值、顺序倒序),旧代码可以使用functools.cmp_to_key()方法转换过来。 参考:https://docs.python.org/3/howto/sorting.html#the-old-way-using-the-cmp-parameter

  • list.reverse() 反转列表中的元素。

  • list.copy() 返回列表的一个浅拷贝,相当于 a[:] 。 多废话几句:所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝。深拷贝只有一种方式:copy 模块中的 deepcopy 函数。

注意到很多只改变 list 的方法没有返回值,这是 Python 中所有可变数据结构的设计原则。

另一件事是并非所有数据都可以排序或比较。例如,[None, ‘hello’, 10] 就不可排序,因为整数不能与字符串比较,而 None 不能与其他类型比较。并且还存在一些没有定义大小关系的类型。例如,3+4j < 5+7j 就不是一个合法的比较。

5.1.1. Using Lists as Stacks

List 方法可以当做堆栈用,“last-in,first-out”,用append在最顶端加元素,用pop方法取出最顶端的元素。

5.1.2. Using Lists as Queues

List 方法也可以当做队列用,“first-in,last-out”,然而 List 方法不太高效,在 list 尾部加减很快,头部操作就很慢。

实现队列最好用 collections.deque。

5.1.3. List Comprehensions

列表推导式,创建新列表的时候用。

例如,创建一个平方数列表可以这么写:

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

但临时变量x不会消失,所以也可以这么写:

squares = list(map(lambda x: x**2, range(10)))

另一种等价写法更清晰:

squares = [x**2 for x in range(10)]

列表推导式的方括号内包含以下内容:一个表达式,后面跟一个 for 子句,然后,是零个或多个 for 或 if 子句。一些例子:

>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1, in <module>
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

5.1.4. Nested List Comprehensions

nested 嵌套的

下面表示了一个转置方法:

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]
>>>
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

实际应用中,最好用内置函数替代复杂的流程语句。此时,zip() 函数更好用:

>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

关于这里的星号,参考 4.7.5. Unpacking Argument Lists

5.2. The del statement

del 语句按索引从列表中移除元素。pop() 方法与之不同,会有返回值。del 语句也可以从列表中移除切片,或清空整个列表(之前的方法是将空列表赋值给切片)。例如:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]

del 也可以用来删除整个变量:

>>> del a

此后,再引用 a 就会报错

5.3. Tuples and Sequences

列表 list 和字符串 string 有很多共同特性,例如索引和切片。他们都属于序列 sequence 数据类型,下文介绍的元组 tuple 也是其中一种。

元组由多个被逗号隔开的值组成,immutable,但可以包含 mutable 的数据类型如列表。

元组输出时有括号,这样可以方便地解释嵌套,但输入时可有可无。

元组 tuple 和列表 list 很像,但使用场景不同:元组一般可包含异质 heterogeneous 元素序列,通过解包 unpack 和索引 index 访问;列表一般包含同质 homogeneous 元素,可迭代 iterate 访问。

构造 0 个或 1 个元素的元组比较特殊:为了适应 accommodate 这种情况,对句法 syntax 有一些额外的改变。用一对空圆括号 parentheses 就可以创建空元组;只有一个元素的元组可以通过在这个元素后添加逗号来构建(enclose a single value in parentheses 的话不够明确)。Ugly, but effective. 例如:

>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

语句 t = 12345, 54321, 'hello!' 是 元组打包 的例子:值 12345, 54321'hello!' 一起被打包进元组。逆操作也可以:x, y, z = t.

称之为序列解包 sequence unpacking。序列解包时,左侧变量与右侧序列元素的数量应相等。注意,多重赋值其实只是元组打包和序列解包的组合。

5.4. Sets

Python 还支持集合 set 数据类型。集合是由不重复元素组成的无序容器 unordered collection with no duplicate elements。

基本用法包括成员检测、消除重复元素。集合对象支持合集 union、交集 intersection、差集 difference、对称差分 symmetric difference 等数学运算。

创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {},后者创建的是空字典,下一小节会介绍字典这种数据结构。

示例:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # fast membership testing
True
>>> 'crabgrass' in basket
False

>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b                              # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # letters in both a and b
{'a', 'c'}
>>> a ^ b                              # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}

>>> # set comprehensions 推导式
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

5.5. Dictionaries

与以连续整数为索引的序列不同,字典以关键字为索引。一个键对应一个值,key-value。

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127 # 通过键存储值
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack'] # 通过键重新赋值
4098
>>> del tel['sape'] # 通过键删除值
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel) # 按插入次序排列键
['jack', 'guido', 'irv']
>>> sorted(tel) # 键排序
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

创建方法:

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) # 用键值对序列创建
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> dict(sape=4139, guido=4127, jack=4098) # 简单字符串为键
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> {x: x**2 for x in (2, 4, 6)} # 推导式
{2: 4, 4: 16, 6: 36}