我正在尝试在Python 3(.6)中使用queue.PriorityQueue
。
我想存储具有给定优先级的对象。但是,如果两个对象具有相同的优先级,则我不介意PriorityQueue.get
返回任何一个。换句话说,我的对象无法以整数进行比较,允许它们成为整数没有意义,我只关心优先级。
在Python 3.7's documentation中,有一个涉及dataclasses
的解决方案。我引用:
如果数据元素不具有可比性,则可以将数据包装在忽略数据项并且仅比较优先级数字的类中:
from dataclasses import dataclass, field
from typing import Any
@dataclass(order=True)
class PrioritizedItem:
priority: int
item: Any=field(compare=False)
A,我使用的是Python 3.6。在the documentation of this version of Python中,对于使用PriorityQueue
设置优先级没有任何评论,也没有对“对象值”感到困扰,这在我的情况下是不合逻辑的。
是否有比在自定义类上定义__le__
和其他比较方法更好的方法?我发现此解决方案特别丑陋且违反直觉,但这可能就是我。
答案 0 :(得分:3)
dataclasses
只是一种便捷的方法,可以避免创建大量样板代码。
您实际上没有创建一个类。具有唯一计数器值的元组也是如此:
from itertools import count
unique = count()
q.put((priority, next(unique), item))
,以使相等优先级之间的联系被后面的整数打破;因为它始终是唯一的,所以永远不会查询item
值。
您还可以使用直接丰富的比较方法来创建类,该方法通过@functools.total_ordering
变得更加简单:
from functools import total_ordering
@total_ordering
class PrioritizedItem:
def __init__(self, priority, item):
self.priority = priority
self.item = item
def __eq__(self, other):
if not isinstance(other, __class__):
return NotImplemented
return self.priority == other.priority
def __lt__(self, other):
if not isinstance(other, __class__):
return NotImplemented
return self.priority < other.priority
答案 1 :(得分:1)
请参见priority queue implementation notes-在您引用的部分之前(关于使用dataclasses
),它告诉您如何进行淘汰:
...将条目存储为3元素列表,包括优先级,条目计数和任务。条目计数是一个平局,因此具有相同优先级的两个任务将按添加顺序返回。并且由于没有两个条目计数相同,所以元组比较将永远不会尝试直接比较两个任务。
因此,在添加到队列中时,只需将您的项作为 3rd 元素添加到元组(Prio, Count, YourElem)
中即可。
有争议的示例:
from queue import PriorityQueue
class CompareError(ValueError): pass
class O:
def __init__(self,n):
self.n = n
def __lq__(self):
raise CompareError
def __repr__(self): return str(self)
def __str__(self): return self.n
def add(prioqueue,prio,item):
"""Adds the 'item' with 'prio' to the 'priorqueue' adding a unique value that
is stored as member of this method 'add.n' which is incremented on each usage."""
prioqueue.put( (prio, add.n, item))
add.n += 1
# no len() on PrioQueue - we ensure our unique integer via method-param
# if you forget to declare this, you get an AttributeError
add.n = 0
h = PriorityQueue()
add(h, 7, O('release product'))
add(h, 1, O('write spec 3'))
add(h, 1, O('write spec 2'))
add(h, 1, O('write spec 1'))
add(h, 3, O('create tests'))
for _ in range(4):
item = h.get()
print(item)
使用h.put( (1, O('write spec 1')) )
会导致
TypeError: '<' not supported between instances of 'O' and 'int'`
使用def add(prioqueue,prio,item):
会推动三胞胎,因为要保证的项目具有明确的第二值,因此我们的O()
实例永远不会被用作平局。
输出:
(1, 2, write spec 3)
(1, 3, write spec 2)
(1, 4, write spec 1)
(3, 5, create tests)
有关更好的唯一第二元素,请参见MartijnPieters答案@here。
答案 2 :(得分:0)
让我们假设我们不想编写一个装饰器,其功能与dataclass
相同。问题在于,我们不必定义所有比较运算符,以使我们的自定义类可根据优先级进行比较。 @functools.total_ordering
装饰器可以提供帮助。摘录:
给定一个定义一个或多个丰富比较排序方法的类,该类装饰器提供其余的内容。这简化了指定所有可能的丰富比较操作所涉及的工作:
该类必须定义
__lt__()
,__le__()
,__gt__()
或__ge__()
中的一个。另外,该类应提供一个__eq__()
方法。
使用提供的示例:
from functools import total_ordering
@total_ordering
class PrioritizedItem:
# ...
def __eq__(self, other):
return self.priority == other.priority
def __lt__(self, other):
return self.priority < other.priority
答案 3 :(得分:0)
您所需要的只是一个包装类,该包装类实现__lt__
以使PriorityQueue
正常工作。记为here:
在两个对象之间进行比较时,保证排序例程使用
__lt__()
。因此,很容易通过定义__lt__()
方法向类添加标准排序顺序
就这么简单
class PriorityElem:
def __init__(self, elem_to_wrap):
self.wrapped_elem = elem_to_wrap
def __lt__(self, other):
return self.wrapped_elem.priority < other.wrapped_elem.priority
如果您的元素没有优先级,那就很简单:
class PriorityElem:
def __init__(self, elem_to_wrap, priority):
self.wrapped_elem = elem_to_wrap
self.priority = other.priority
def __lt__(self, other):
return self.priority < other.priority
现在您可以像这样使用PriorityQueue
queue = PriorityQueue()
queue.put(PriorityElem(my_custom_class1, 10))
queue.put(PriorityElem(my_custom_class2, 10))
queue.put(PriorityElem(my_custom_class3, 30))
first_returned_elem = queue.get()
# first_returned_elem is PriorityElem(my_custom_class1, 10)
second_returned_elem = queue.get()
# second_returned_elem is PriorityElem(my_custom_class2, 10)
third_returned_elem = queue.get()
# third_returned_elem is PriorityElem(my_custom_class3, 30)
在这种情况下,获取原始元素就像
一样简单elem = queue.get().wrapped_elem
由于您不在乎排序稳定性,所以您只需要
。编辑:如注释和confirmed here中所述,heappush
不稳定:
与sorted()不同,此实现不稳定。