序列构成的数组

Posted yuanoung

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了序列构成的数组相关的知识,希望对你有一定的参考价值。

[TOC]

内置序列类型概览

技术分享图片

列表推导和生成器表达式

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

其他的队列有空在总结.



































以上是关于序列构成的数组的主要内容,如果未能解决你的问题,请参考以下文章

第二章 序列构成的数组

流畅的python序列构成的数组

序列构成的数组

流畅的python第二章序列构成的数组学习记录

染色数组

翻转数组