内置序列类型概览

列表推导和生成器表达式

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

其他的队列有空在总结.

版权声明:本文为yuanoung原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yuanoung/p/8440719.html