序列构成的数组
内置序列类型概览
列表推导和生成器表达式
In [20]: {s for s in "sstringg"} # 集合推导
Out[20]: {\'g\', \'i\', \'n\', \'r\', \'s\', \'t\'}
In [25]: [s for s in "string" if s != "i"]
Out[25]: [\'s\', \'t\', \'r\', \'n\', \'g\']
In [27]: {s:i for i, s in enumerate("string")}
Out[27]: {\'g\': 5, \'i\': 3, \'n\': 4, \'r\': 2, \'s\': 0, \'t\': 1}
In [28]: (s for s in "string" if s != "i")
Out[28]: <generator object <genexpr> at 0x7fd844195fc0>
In [29]: list(s for s in "string" if s != "i") # 这里只需要一个括号
Out[29]: [\'s\', \'t\', \'r\', \'n\', \'g\']
元组
元组拆包
In [30]: a, b, *rest = range(5)
In [31]: a, b, *rest # rest均是一个list,无论有没有值
Out[31]: (0, 1, 2, 3, 4)
In [32]: a, b, *rest = range(3)
In [33]: a, b, *rest
Out[33]: (0, 1, 2)
In [34]: rest
Out[34]: [2]
In [35]: a, b, *rest = range(2)
In [36]: rest
Out[36]: []
具名元组
collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类——这个带名字的类对调试程序有很大帮助。
In [1]: from collections import namedtuple
In [2]: City = namedtuple(\'City\', \'name coutry population coordinates\')
In [3]: tokyo = City(\'Tokyo\', \'JP\', 36.933, (35.689722, 139.691667))
In [4]: tokyo.name # 可以通过属性,以及索引来获取值.
Out[4]: \'Tokyo\'
In [5]: tokyo["name"]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-b68fdbfa4b27> in <module>()
----> 1 tokyo["name"]
TypeError: tuple indices must be integers or slices, not str
In [6]: tokyo[1]
Out[6]: \'JP\'
In [7]: City._fields # 包含这个类所有字段名称的元组
Out[7]: (\'name\', \'coutry\', \'population\', \'coordinates\')
In [8]: LatLong = namedtuple(\'LatLong\', \'lat long\')
In [9]: City = namedtuple(\'City\', \'name coutry population coordinates\')
In [10]: delhi_data = (\'Delhi NCR\', \'IN\', 21.935, LatLong(28.613889, 77.208889))
In [11]: delhi = City._make(delhi_data) # 接受一个可迭代对象来生成这个类的一个实例,等于City(*delhi_data)
In [12]: delhi._asdict() # 具名元组以 collections.OrderedDict 的形式返回
Out[12]:
OrderedDict([(\'name\', \'Delhi NCR\'),
(\'coutry\', \'IN\'),
(\'population\', 21.935),
(\'coordinates\', LatLong(lat=28.613889, long=77.208889))])
In [13]: for key, value in delhi._asdict().items():
...: print(key + \':\', value)
...:
name: Delhi NCR
coutry: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)
方法 | 列表 | 元组 | 说明 |
---|---|---|---|
s.__add__(s2) |
• | • | s + s2,拼接 |
s.__iadd__(s2) |
• | s += s2,就地拼接 | |
s.append(e) |
• | 在尾部添加一个新元素 | |
s.clear() |
• | 删除所有元素 | |
s.__contains__(e) |
• | • | s 是否包含 e |
s.copy() |
• | 列表的浅复制 | |
s.count(e) |
• | • | e 在 s 中出现的次数 |
s.__delitem__(p) |
• | 把位于 p 的元素删除 | |
s.extend(it) |
• | 把可迭代对象 it 追加给 s | |
s.__getitem__(p) |
• | • | s[p],获取位置 p 的元素 |
s.__getnewargs__() |
• | 在 pickle 中支持更加优化的序列化 | |
s.index(e) |
• | • | 在 s 中找到元素 e 第一次出现的位置 |
s.insert(p, e) |
• | 在位置 p 之前插入元素e | |
s.__iter__() |
• | • | 获取 s 的迭代器 |
s.__len__() |
• | • | len(s),元素的数量 |
s.__mul__(n) |
• | • | s * n, n 个 s 的重复拼接 |
s.__imul__(n) |
• | s *= n,就地重复拼接 | |
s.__rmul__(n) |
• | • | n * s,反向拼接 * |
s.pop([p]) |
• | 删除最后或者是(可选的)位于 p 的元素,并返回它的值 | |
s.remove(e) |
• | 删除 s 中的第一次出现的 e | |
s.reverse() |
• | 就地把 s 的元素倒序排列 | |
s.__reversed__() |
• | 返回 s 的倒序迭代器 | |
s.__setitem__(p, e) |
• | s[p] = e,把元素 e放在位置p,替代已经在那个位置的元素 | |
s.sort([key],[reverse]) |
• | 就地对s中的元素进行排序,可选的参数有键(key)和是否倒序( reverse) |
对序列使用+和*
In [14]: board = [[\'_\'] * 3 for i in range(3)]
In [15]: board
Out[15]: [[\'_\', \'_\', \'_\'], [\'_\', \'_\', \'_\'], [\'_\', \'_\', \'_\']]
In [16]: board[1][2]
Out[16]: \'_\'
In [17]: board[1][2] = \'X\'
In [18]: board
Out[18]: [[\'_\', \'_\', \'_\'], [\'_\', \'_\', \'X\'], [\'_\', \'_\', \'_\']]
上面的代码等价于:
In [23]: def f():
...: board = []
...: for i in range(3):
...: row = [\'_\'] * 3
...: board.append(row)
而下面的代码会出现预想不到的结果:
In [19]: weird_board = [[\'_\'] * 3] * 3
In [20]: weird_board
Out[20]: [[\'_\', \'_\', \'_\'], [\'_\', \'_\', \'_\'], [\'_\', \'_\', \'_\']]
In [21]: weird_board[1][2] = \'X\'
In [22]: weird_board
Out[22]: [[\'_\', \'_\', \'X\'], [\'_\', \'_\', \'X\'], [\'_\', \'_\', \'X\']]
等价于:
row=[\'_\'] * 3
board = []
for i in range(3):
board.append(row)
序列的增量赋值
变量名会不会被关联到新的对象,完全取决于这个类型有没有实现
__iadd__
这个方法.
什么意思?对于
a += b
- 如果 a 实现了
__iadd__
方法,就会调用这个方法。同时对可变序列(例如list、 bytearray 和 array.array)来说, a 会就地改动,就像调用了a.extend(b)
一样。 - 如果 a 没有实现
__iadd__
的话,a += b
这个表达式的效果就变得跟a = a+ b
一样了:首先计算 a + b,得到一个新的对象,然后赋值给 a。 - 也就是说,在这个表达式中,变量名会不会被关联到新的对象,完全取决于这个类型有没有实现
__iadd__
(类是的其他方法,__imul__
)这个方法。
对于元组里的可变列表进行+=操作
In [25]: t = (1, 2, [30, 40])
In [26]: t[2] += [50, 60]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-26-d877fb0e9d36> in <module>()
----> 1 t[2] += [50, 60]
TypeError: \'tuple\' object does not support item assignment
In [27]: t
Out[27]: (1, 2, [30, 40, 50, 60])
得到的结论是:
- 会改变
- 并且会抛出异常
- 增量赋值不是一个原子操作
list.sort方法和内置函数sorted
- list.sort 方法会就地排序列表,也就是说不会把原列表复制一份。这也是这个方法的返回值是 None 的原因,提醒你本方法不会新建一个列表。
- list.sort 相反的是内置函数 sorted,它会新建一个列表作为返回值。
两个可选的关键字参数
- reverse, True or False, default=False
- key, 对比关键字.
key=str.lower
来实现忽略大小写的排序,或者是用key=len
进行基于字符串长度的排序。
用bisect来管理已排序的序列
利用二分查找算法来在有序序列中查找或插入元素
import bisect
bisect.bisect(list, val)
bisect 函数其实是 bisect_right 函数的别名,后者还有个姊妹函数叫bisect_left。它们的区别在于, bisect_left 返回的插入位置是原序列中跟被插入元素相等的元素的位置,也就是新元素会被放置于它相等的元素的前面,而 bisect_right返回的则是跟它相等的元素之后的位置。
bisect.insort(seq, item)
把变量 item 插入到序列 seq 中
当列表不是首选时
array
>>> from array import array ➊
>>> from random import random
>>> floats = array(\'d\', (random() for i in range(10**7))) ➋
>>> floats[-1] ➌
0.07802343889111107
>>> fp = open(\'floats.bin\', \'wb\')
>>> floats.tofile(fp) ➍
>>> fp.close()
>>> floats2 = array(\'d\') ➎
>>> fp = open(\'floats.bin\', \'rb\')
>>> floats2.fromfile(fp, 10**7) ➏
>>> fp.close()
>>> floats2[-1] ➐
0.07802343889111107
>>> floats2 == floats
True
方法 | 列表 | 数组 | 说明 |
---|---|---|---|
s.__add(s2)__ |
• | • | s + s2,拼接 |
s.__iadd(s2)__ |
• | • | s += s2,就地拼接 |
s.append(e) | • | • | 在尾部添加一个元素 |
s.byteswap | • | 翻转数组内每个元素的字节序列,转换字节序 | |
s.clear() | • | 删除所有元素 | |
s.__contains__(e) |
• | • | s 是否含有 e |
s.copy() | • | 对列表浅复制 | |
s.__copy__() |
• | 对 copy.copy 的支持 | |
s.count(e) | • | • | s 中 e 出现的次数 |
s.__deepcopy__() |
• | 对 copy.deepcopy 的支持 | |
s.__delitem__(p) |
• | • | 删除位置 p 的元素 |
s.extend(it) | • | • | 将可迭代对象 it 里的元素添加到尾部 |
s.frombytes(b) | • | 将压缩成机器值的字节序列读出来添加到尾部 | |
s.fromfile(f, n) | • | 将二进制文件 f 内含有机器值读出来添加到尾部,最多添加 n 项 | |
s.fromlist(l) | • | 将列表里的元素添加到尾部,如果其中任何一个元素导致了 TypeError异常,那么所有的添加都会取消 | |
s.__getitem__(p) |
• | • | s[p],读取位置 p 的元素 |
s.index(e) | • | • | 找到 e 在序列中第一次出现的位置 |
s.insert(p, e) | • | • | 在位于 p 的元素之前插入元素 e |
s.itemsize | • | 数组中每个元素的长度是几个字节 | |
s.__iter__() |
• | • | 返回迭代器 |
s.__len__() |
• | • | len(s),序列的长度 |
s.__mul__(n) |
• | • | s * n,重复拼接 |
s.__imul__(n) |
• | • | s *= n,就地重复拼接 |
s.__rmul__(n) |
• | • | n * s,反向重复拼接* |
s.pop([p]) | • | • | 删除位于 p 的值并返回这个值, p 的默认值是最后一个元素的位置 |
s.remove(e) | • | • | 删除序列里第一次出现的 e 元素 |
s.reverse() | • | • | 就地调转序列中元素的位置 |
s.__reversed__() |
• | 返回一个从尾部开始扫描元素的迭代器 | |
s.__setitem__(p,e) |
• | • | s[p] = e,把位于 p 位置的元素替换成 e |
s.sort([key],[revers]) | • | 就地排序序列,可选参数有 key 和 reverse | |
s.tobytes() | • | 把所有元素的机器值用 bytes 对象的形式返回 | |
s.tofile(f) | • | 把所有元素以机器值的形式写入一个文件 | |
s.tolist() | • | 把数组转换成列表,列表里的元素类型是数字对象 | |
s.typecode | • | 返回只有一个字符的字符串,代表数组元素在 C 语言中的类型 |
内存视图
memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片。 这个功能在处理大型数据集合的时候非常重要。
>>> numbers = array.array(\'h\', [-2, -1, 0, 1, 2])
>>> memv = memoryview(numbers) ➊
>>> len(memv)
5
>>> memv[0] ➋
-2
>>> memv_oct = memv.cast(\'B\') ➌
>>> memv_oct.tolist() ➍
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
>>> memv_oct[5] = 4 ➎
>>> numbers
array(\'h\', [-2, -1, 1024, 1, 2])
双向队列和其他形式的队列
但是删除列表的第一个元素(抑或是在第一个元素之前添加一个元素)之类的操作是很耗时的,因为这些操作会牵扯到移动列表里的所有元素。
collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型。
>>> from collections import deque
>>> dq = deque(range(10), maxlen=10) ➊
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3) ➋
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1) ➌
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11, 22, 33]) ➍
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
>>> dq.extendleft([10, 20, 30, 40]) ➎
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)
方法 | 列表 | 双向队列 | 说明 |
---|---|---|---|
s.__add__(s2) |
• | s + s2,拼接 | |
s.__iadd__(s2) |
• | • s += s2,就地拼接 | |
s.append(e) | • | • | 添加一个元素到最右侧(到最后一个元素之后) |
s.appendleft(e) | • | 添加一个元素到最左侧(到第一个元素之前) | |
s.clear() | • | • | 删除所有元素 |
s.__contains__(e) |
• | s 是否含有 e | |
s.copy() | • | 对列表浅复制 | |
s.__copy__() |
• | 对 copy.copy(浅复制)的支持 | |
s.count(e) | • | • | s 中 e 出现的次数 |
s.__delitem__(p) |
• | • | 把位置 p 的元素移除 |
s.extend(i) | • | • | 将可迭代对象 i 中的元素添加到尾部 |
s.extendleft(i) | • | 将可迭代对象 i 中的元素添加到头部 | |
s.__getitem__(p) |
• | • | s[p],读取位置 p 的元素 |
s.index(e) | • | 找到 e 在序列中第一次出现的位置 | |
s.insert(p, e) | • | 在位于 p 的元素之前插入元素 e | |
s.__iter__() |
• | • | 返回迭代器 |
s.__len__() |
• | • | len(s),序列的长度 |
s.__mul__(n) |
• | s * n,重复拼接 | |
s.__imul__(n) |
• | s *= n,就地重复拼接 | |
s.__rmul__(n) |
• | n * s,反向重复拼接* | |
s.pop() | • | • | 移除最后一个元素并返回它的值# |
s.popleft() | • | 移除第一个元素并返回它的值 | |
s.remove(e) | • | • | 移除序列里第一次出现的 e 元素 |
s.reverse() | • | • | 调转序列中元素的位置 |
s.__reversed__() |
• | • | 返回一个从尾部开始扫描元素的迭代器 |
s.rotate(n) | • | 把 n 个元素从队列的一端移到另一端 | |
s.__setitem__(p, e) |
• | • | s[p] = e,把位于 p 位置的元素替换成 e |
s.sort([key], [revers]) | • | 就地排序序列,可选参数有 key 和 reverse |
其他的队列有空在总结.