我能否从PriorityQueue中获取一个项而不将其删除?

60

我想获取队列中的下一个项目,但是我不想将其出队。在Python的queue.PriorityQueue中是否可以实现?根据文档,我没有看到如何实现。

7个回答

73
如果a是PriorityQueue对象,您可以使用a.queue[0]获取下一个项目:
from queue import PriorityQueue

a = PriorityQueue()

a.put((10, "a"))
a.put((4, "b"))
a.put((3,"c"))

print(a.queue[0])
print(a.queue)
print(a.get())
print(a.queue)
print(a.get())
print(a.queue)

输出结果为:

(3, 'c')
[(3, 'c'), (10, 'a'), (4, 'b')]
(3, 'c')
[(4, 'b'), (10, 'a')]
(4, 'b')
[(10, 'a')]

但要注意多线程访问的问题。


10
似乎 q.queue[0] 返回队列中最高优先级的项目,但 q.queue[1] 不一定返回第二高优先级的项目。 - Rufus
13
根据优先队列的理论,您会发现第二高的优先级是q.queue[1]或者q.queue[2]。这是因为父节点(q.queue[0]在本例中)必须具有比它的两个子节点(q.queue[1]q.queue[2])更高的优先级,但这两个子节点的特定顺序并不重要。这意味着整个q.queue不是绝对排序的,只是“堆”排序(即每个层级都具有比它下面的层级更高的优先级)。 - Marawan Okasha
3
@SobirBobiev 不行。队列使用a.queue作为存储器。列表是队列在内存中以明文形式存储的方式。只要队列被构建,列表就必须存在。直接访问列表会绕过整个队列的逻辑,直接进入其存储。 - foki
1
@SobirBobiev 因此,a.queue[0] 是常数时间。 - foki
1
@gebbissimo 在多线程访问中,您不能假设顶部项目将保持为顶部项目,因为其他线程可能会操作PriorityQueue。请参阅我的答案以获取示例:https://dev59.com/4mox5IYBdhLWcg3wTypx#72277696 - Stephen C
显示剩余7条评论

8
如果您想按照元素插入的顺序获取PriorityQueue中的下一个元素,请使用以下代码:
for i in range(len(queue.queue)):
    print queue.queue[i]

这不会弹出任何东西。

如果您希望按优先顺序进行排序,请使用:

for i in range(len(queue.queue)):
    temp = queue.get()
    queue.put(temp)
    print temp

如果您使用的是元组而不是单个变量,请将temp替换为:
((temp1,temp2))

这个解决方案不仅限于 PriorityQueue 对象,也适用于 Queue 对象。在我看来,这似乎是最优雅的解决方案。请勿误解,但我认为其他答案无法与此相提并论(仅代表个人意见)。 - MikeyE
第一部分不会按照插入顺序给出元素,但第一个元素将是具有最低值的元素。 - Atnas

4
假设你在 PriorityQueue 中存储的项是一个元组 (优先级, 值),
def peek(pq):
  return pq.queue[0][1] 

3
队列的第一个元素应该可以被索引到。如果你正在使用heapq库,那么文档中提到:
堆的有趣属性是最小元素总是根节点,heap[0]

2

根据理论,当您从队列中取出项目时,它将从队列中删除。您需要编写自己的函数,该函数可以为您提供PriorityQueue的最后一个元素。您可以通过继承priorityqueue来创建peek函数。


假设我扩展PriorityQueue,我仍然需要访问底层的数据存储来实现peak,但是如何访问呢? - Jiew Meng
1
如果您可以检查代码http://hg.python.org/cpython/file/2.7/Lib/Queue.py,那么它们使用列表来存储数据。因此,您可以像示例中的`self.queue`一样随意操作列表。此外,您还可以检查PriorityQueue的`_get`方法,以便在需要更改该功能时覆盖该函数。 - Nilesh
1
cpython和python是一样的吗? - Jiew Meng

0

简而言之 -- 如果您在多线程环境中多次使用顶部项目,则应将该项目分配给变量。


以上答案展示了如何访问PriorityQueue的元素,然而在多线程方式下从内部队列中访问对象存在一定的危险(正如HYRY's answer末尾所提到的)。

虽然持有queue对象会更不安全,因为该queue对象是“活动的”(参见this answer),但如果您期望对象排序是静态的,则仍可能遇到访问队列上的对象时出现问题。

以下是一个例子:

from queue import PriorityQueue          
import threading                         
import time                              
                                         
pq = PriorityQueue()                     
                                         
def queue_manip():                       
    i = 0                                
    while True:                          
        pq.put((i, i))                   
        i -= 1                           
        time.sleep(.1)                   
                                         
t1 = threading.Thread(target=queue_manip)
t1.start()                               
                                         
while True:                              
    print('FirstAccess:  ', pq.queue[0]) 
    time.sleep(.2)  # other processing   
    print('SecondAccess: ', pq.queue[0]) 
    time.sleep(.2)  # other processing   
    print()                              

这将会打印出类似下面的内容:

FirstAccess:   (0, 0)  
SecondAccess:  (-1, -1)
                       
FirstAccess:   (-3, -3)
SecondAccess:  (-5, -5)
                       
FirstAccess:   (-7, -7)
SecondAccess:  (-9, -9)

因此,如果您想使用来自PriorityQueue顶部的对象,您应该将该对象分配一个名称:

from queue import PriorityQueue          
import threading                         
import time                              
                                         
pq = PriorityQueue()                     
                                         
def queue_manip():                       
    i = 0                                
    while True:                          
        pq.put((i, i))                   
        i -= 1                           
        time.sleep(.1)                   
                                         
t1 = threading.Thread(target=queue_manip)
t1.start()                               
                 
while True:                           
    priority, item = pq.queue[0]      
    print('FirstUse:  ', item)        
    time.sleep(.2)  # other processing
    print('SecondUse: ', item)        
    time.sleep(.2)  # other processing
    print()                           
                                      

这将为您提供:

FirstUse:   0
SecondUse:  0

FirstUse:   -3
SecondUse:  -3

FirstUse:   -7
SecondUse:  -7

0
如果 q 是优先队列,那么可以使用以下代码:
for i in range(q.qsize()):
    print(q.queue[i])

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