如何在不删除项目的情况下获取队列中的项目?

37

get() 是 Python 中从 Queue 移除并返回项的方法。

import queue

q = queue.Queue() # Here

q.put("Apple")
q.put("Orange")
q.put("Banana")

print(q.get())
print(q.get())
print(q.get())

输出:

Apple
Orange
Banana

现在,我想要获取 Queue 中的元素而不将它们移除。

这个能够实现吗?


5
听起来你应该使用collections.deque,你是否正在使用线程? - jamylak
我不使用线程。 - damon
5个回答

34

queue_object.queue将返回一个deque对象中的队列副本,您随后可以使用该队列的切片。当然,它与原始队列不同步,但将允许您在复制时查看队列。

为什么您不想这样做有一个很好的解释,在此线程中详细说明:comp.lang.python - Queue peek?。但是,如果您只是想了解Queue如何工作,这是一种简单的方法。

import Queue
q = Queue.Queue()
q.push(1)
q.put('foo')
q.put('bar')
d = q.queue
print(d)
deque(['foo', 'bar'])
print(d[0])
'foo'

18
因为我正确回答了他的问题,你就给我点了-1吗?这真的很无礼。你没有考虑到他可能正在使用需要队列互斥锁的现有代码,并且他只是试图理解它的工作方式-以观察队列。忽视这个问题(这是一个合理的新手问题),或者给我的正确答案点-1,都违背了stackoverflow的精神和帮助他人的初衷。 - synthesizerpatel
我选择-1d,因为你没有正确回答问题,也没有提到如何删除它,而且这个任务并不适合使用队列互斥锁(正如评论中所述,OP并不需要队列互斥锁)。 - jamylak
10
可以。我已经解释了如何做到这一点。对于提问者来说,他可以转换为deque,但是其他已有代码库的人可能无法这样做。 - synthesizerpatel
我需要检查队列头部的项是否满足某些条件,如果满足,则将其从队列中移除。同样地,我需要检查队列中的任何其他项是否满足类似的条件,并将其移除。 - jamylak
1
虽然 Queue 对象的 .queue 属性没有前导 _ 表示它是私有的,但它是未记录的,因此我不愿使用它。我不会对你的回答进行负面评价,但我认为这不是一个好建议。 - martineau
21
你们两个都是对的。stackoverflow存在一个严重的问题,就是不清楚回答应该是严谨的(“你的问题暗示了你在做错事”)还是务实的(“这就是你所要求的不安全方法的实现方式”)。正确的建议是:一定要两者兼顾。1. 提供不安全的方法,2. 解释为什么不应该使用它。当回答问题时,你不知道别人提出问题的原因。 - Erik Aronesty

14

Queue 模块实现了多生产者、多消费者队列。在线程编程中,当多个线程之间需要安全地交换信息时,它尤其有用。

正如您所见,Queue 模块是专门为了与线程一起使用而创建的,提供的仅有 FIFOLIFO优先级队列,都没有提供此功能。然而,通过查看 Queue 模块的源代码,可以看到它只是使用了一个collections.deque(双端队列),它可以轻松完成您的任务。您可以使用常数时间对第一项进行索引 ([0]) 并使用 .popleft() 方法。


为了避免误解,也许你应该提到(对于其他读者)Queue有一个self.mutex = _threading.Lock(),但是deque没有,因此在多线程环境中使用后者而不是前者是不等价的——这里并非如此,因为OP在评论中提到没有使用线程。 - martineau

9

直接访问底层队列是不安全的。

安全的方法是扩展Queue类。如果返回基础的deque对象,则你将得到实时对象而非拷贝

这样做的结果是,在你迭代期间,它可能会发生更改-如果另一个线程在你迭代期间插入队列,则会导致异常。

Python使用GIL,因此您可以安全地使用list(q.queue),因为list()永远不会引起上下文切换。

最好使用与get()函数相同的锁,并且不要对GIL作出假设:

import queue
    
class SnapshotQueue(queue.Queue):
    def snapshot(self):
        with self.mutex:
            return list(self.queue)

该类可以安全地替代普通队列,并在互斥锁内返回队列状态的快照,而不会影响底层队列操作。


1
提示:你在with行缺少冒号。但这是一个好答案。 - Nickleman

1
您可以按照以下方式获取队列中的项,而无需删除这些项:
import queue

q = queue.Queue()

q.put("Apple")
q.put("Orange")
q.put("Banana")

print(q.queue[0]) # Here
print(q.queue[1]) # Here
print(q.queue[2]) # Here

print(q.queue) # Here

输出:

Apple
Orange
Banana
deque(['Apple', 'Orange', 'Banana'])

而且,您还可以像下面展示的那样更改队列中的项目:

import queue

q = queue.Queue()

q.put("Apple")
q.put("Orange")
q.put("Banana")

q.queue[0] = "Strawberry" # Here
q.queue[1] = "Lemon"      # Here
q.queue[2] = "kiwi"       # Here

print(q.queue[0])
print(q.queue[1])
print(q.queue[2])

print(q.queue)

输出:

Strawberry
Lemon
kiwi
deque(['Strawberry', 'Lemon', 'kiwi'])

但是,如果没有put(),你无法向队列中添加元素,如下所示:

import queue

q = queue.Queue()

q.queue[0] = "Apple"  # Cannot add
q.queue[1] = "Orange" # Cannot add
q.queue[2] = "Banana" # Cannot add

print(q.queue[0])
print(q.queue[1])
print(q.queue[2])

print(q.queue)

然后,出现了以下错误:

IndexError: deque索引超出范围


0
我发现这个问题,因为我需要一种访问PriorityQueue中顶部元素的方法。我找不到这样做的方法,所以我转而使用heapq。虽然值得一提的是,heapq不是线程安全的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接