作为Uri Goren 精明的noted above,Python 标准库已经代表您实现了一个高效的队列:collections.deque。
什么不该做
避免自己动手重新发明轮子:
-
Linked list implementation。虽然这样做可以将
dequeue() 和 enqueue() 方法的最坏情况时间复杂度降低到 O(1),但 collections.deque 类型已经这样做了。鉴于其基于 C 的传统,它也是线程安全的,并且可能更节省空间和时间。
-
Python list implementation。正如我note below 一样,根据 Python 列表实现
enqueue() 方法会将其最坏情况的时间复杂度增加到 O(n)。 因为从基于 C 的数组中删除最后一项并且因此 Python 列表是一个恒定时间操作,根据 Python 列表实现 dequeue() 方法保留了相同的最坏情况时间复杂度 O(1)。但谁在乎? enqueue() 仍然慢得可怜。
引用official deque documentation:
虽然list 对象支持类似的操作,但它们针对快速固定长度操作进行了优化,并且对于pop(0) 和insert(0, v) 操作会产生 O(n) 内存移动成本,这些操作会同时改变基础数据的大小和位置表示。
更重要的是,deque 还通过在初始化时传递的 maxlen 参数提供了对最大长度的开箱即用支持,无需手动尝试限制队列大小(由于 if 条件中隐含的竞争条件,这不可避免地会破坏线程安全)。
做什么
相反,按照标准collections.deque 类型实现您的Queue 类,如下所示:
from collections import deque
class Queue:
'''
Thread-safe, memory-efficient, maximally-sized queue supporting queueing and
dequeueing in worst-case O(1) time.
'''
def __init__(self, max_size = 10):
'''
Initialize this queue to the empty queue.
Parameters
----------
max_size : int
Maximum number of items contained in this queue. Defaults to 10.
'''
self._queue = deque(maxlen=max_size)
def enqueue(self, item):
'''
Queues the passed item (i.e., pushes this item onto the tail of this
queue).
If this queue is already full, the item at the head of this queue
is silently removed from this queue *before* the passed item is
queued.
'''
self._queue.append(item)
def dequeue(self):
'''
Dequeues (i.e., removes) the item at the head of this queue *and*
returns this item.
Raises
----------
IndexError
If this queue is empty.
'''
return self._queue.pop()
证据就在地狱布丁中:
>>> queue = Queue()
>>> queue.enqueue('Maiden in Black')
>>> queue.enqueue('Maneater')
>>> queue.enqueue('Maiden Astraea')
>>> queue.enqueue('Flamelurker')
>>> print(queue.dequeue())
Flamelurker
>>> print(queue.dequeue())
Maiden Astraea
>>> print(queue.dequeue())
Maneater
>>> print(queue.dequeue())
Maiden in Black
一个人去很危险
实际上,也不要这样做。
您最好只使用原始的deque 对象,而不是尝试手动将该对象封装在Queue 包装器中。上面定义的Queue 类仅作为deque API 的通用实用程序的简单演示。
deque 类提供significantly more features,包括:
...迭代、酸洗、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d)、使用 in 运算符的成员资格测试以及下标引用,例如 d[-1]。
只需在需要单端或双端队列的任何地方使用deque。就是这样。