流畅的 Python(34):序列构成的数组 2.9.4

阅读数:12 2019 年 11 月 20 日 17:15

流畅的Python(34):序列构成的数组 2.9.4

内容简介
本书致力于帮助 Python 开发人员挖掘这门语言及相关程序库的优秀特性,避免重复劳动,同时写出简洁、流畅、易读、易维护,并且具有地道 Python 风格的代码。本书尤其深入探讨了 Python 语言的高级用法,涵盖数据结构、Python 风格的对象、并行与并发,以及元编程等不同的方面。

(双向队列和其他形式的队列)

利用 .append.pop 方法,我们可以把列表当作栈或者队列来用(比如,把 .append.pop(0) 合起来用,就能模拟栈的“先进先出”的特点)。但是删除列表的第一个元素(抑或是在第一个元素之前添加一个元素)之类的操作是很耗时的,因为这些操作会牵扯到移动列表里的所有元素。

collections.deque 类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型。而且如果想要有一种数据类型来存放“最近用到的几个元素”,deque 也是一个很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素。示例 2-23 中有几个双向队列的典型操作。

示例 2-23 使用双向队列

>>> 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)

maxlen 是一个可选参数,代表这个队列可以容纳的元素的数量,而且一旦设定,这个属性就不能修改了。

❷ 队列的旋转操作接受一个参数 n,当 n > 0 时,队列的最右边的 n 个元素会被移动到队列的左边。当 n < 0 时,最左边的 n 个元素会被移动到右边。

❸ 当试图对一个已满(len(d) == d.maxlen)的队列做尾部添加操作的时候,它头部的元素会被删除掉。注意在下一行里,元素 0 被删除了。

❹ 在尾部添加 3 个元素的操作会挤掉 -112

extendleft(iter) 方法会把迭代器里的元素逐个添加到双向队列的左边,因此迭代器里的元素会逆序出现在队列里。

表 2-3 总结了列表和双向队列这两个类型的方法(object 类包含的方法除外)。

双向队列实现了大部分列表所拥有的方法,也有一些额外的符合自身设计的方法,比如说 popleftrotate。但是为了实现这些方法,双向队列也付出了一些代价,从队列中间删除元素的操作会慢一些,因为它只对在头尾的操作进行了优化。

appendpopleft 都是原子操作,也就说是 deque 可以在多线程程序中安全地当作先进先出的栈使用,而使用者不需要担心资源锁的问题。

表 2-3:列表和双向队列的方法(不包括由对象实现的方法)

列表 双向队列
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) se 出现的次数
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]) 就地排序序列,可选参数有 keyreverse

* 第 13 章会讲反向运算符。

# a_list.pop(p) 这个操作只能用于列表,双向队列的这个方法不接收参数。

除了 deque 之外,还有些其他的 Python 标准库也有对队列的实现。

queue

提供了同步(线程安全)类 QueueLifoQueuePriorityQueue,不同的线程可以利用这些数据类型来交换信息。这三个类的构造方法都有一个可选参数 maxsize,它接收正整数作为输入值,用来限定队列的大小。但是在满员的时候,这些类不会扔掉旧的元素来腾出位置。相反,如果队列满了,它就会被锁住,直到另外的线程移除了某个元素而腾出了位置。这一特性让这些类很适合用来控制活跃线程的数量。

multiprocessing

这个包实现了自己的 Queue,它跟 queue.Queue 类似,是设计给进程间通信用的。同时还有一个专门的 multiprocessing.JoinableQueue 类型,可以让任务管理变得更方便。

asyncio

Python 3.4 新提供的包,里面有 QueueLifoQueuePriorityQueueJoinableQueue,这些类受到 queuemultiprocessing 模块的影响,但是为异步编程里的任务管理提供了专门的便利。

heapq

跟上面三个模块不同的是,heapq 没有队列类,而是提供了 heappushheappop 方法,让用户可以把可变序列当作堆队列或者优先队列来使用。

到了这里,我们对列表之外的类的介绍也就告一段落了,是时候阶段性地总结一下对序列类型的探索了。注意我们还没有提到 str(字符串)和二进制序列,它们将在第 4 章中专门介绍。

图灵地址 https://www.ituring.com.cn/book/1564

评论

发布