是否可以在python中将比较器传递给PriorityQueue

flseospp  于 2023-02-26  发布在  Python
关注(0)|答案(2)|浏览(155)

我想使用一个PriorityQueue,我需要添加一个类的对象(比如节点),我不能修改。我需要这些对象的优先级基于对象的字段。
我试着将它们作为元组(瓦尔,node)添加到队列中,但它仍然给我“TypeError:“Node”和“Node”的示例之间不支持“〈”“。它仍比较”node“对象以解析关系。

a = Node(3)
b = Node(4)

q = queue.PriorityQueue()
q.put(a)
q.put(b) // Errors TypeError: '<' not supported between instances of 'Node' and 'Node'

q.put((a.val, a))
q.put((b.val, b)) // Errors TypeError: '<' not supported between instances of 'Node' and 'Node'

我知道这个错误的原因是Node对象需要相互比较,根据文档,实现这个目的的方法是至少为Node实现lteq方法。https://docs.python.org/3.1/library/stdtypes.html#comparisons。
但是对于我的问题,由于我不能修改Node类,我是否能够向PriorityQueue传递一种方法(lambda或Comparator)以帮助它确定排序(我期待类似Java的东西)。
q = PriorityQueue(comparator)
任何实现这一点的替代方案也是值得赞赏的。(记住节点是不可修改的)

emeijp43

emeijp431#

一种解决方案是不传递比较器,将Node对象 Package 在另一个对象中,比如ComparableNode,它实现了您希望在Node对象上进行的比较。
假设您有以下设置:

class Node:
    def __init__(self, val):
        self.val = val

    def __repr__(self):
        return "Node(val={})".format(self.val)

a = Node(3)
b = Node(4)
c = Node(4)

但是你不能修改Node,所以你可以把它 Package 在你创建的类里。

@functools.total_ordering
class ComparableNode(Node):
    def __gt__(self, other):
        return self.val > other.val

    def __eq__(self, other):
        return self.val == other.val

aa = ComparableNode(3)
bb = ComparableNode(4)
cc = ComparableNode(4)

或者如果只想传递已经存在的Node对象:

@functools.total_ordering
class ComparableNode:
    def __init__(self, node):
        self.node = node

    def __gt__(self, other):
        return self.node.val > other.node.val

    def __eq__(self, other):
        return self.node.val == other.node.val

    def __repr__(self):
        return repr(self.node)

aa = ComparableNode(a)
bb = ComparableNode(b)
cc = ComparableNode(c)

然后正常添加:

p = PriorityQueue()
p.put(aa)
p.put(bb)
p.put(cc)

p.queue  # [Node(val=3), Node(val=4), Node(val=4)]

另一种方法是,在推入每个元素时指定其优先级。例如,heapq就是这样。
使用heapq时:

import heapq

q = []
heapq.heappush(q, (a.val, a))
heapq.heappush(q, (b.val, b))

q  # [(3, Node(val=3)), (4, Node(val=4))]

对于queue.PriorityQueue,请按照文档的建议使用元组

from queue import PriorityQueue

p = PriorityQueue()
p.put((a.val, a))
p.put((b.val, b))

p.queue  # [(3, Node(val=3)), (4, Node(val=4))]

如果有重复值,则比较将查看元组的第二个元素,并且由于Node不可比较而中断。在这种情况下,这取决于处理重复值的方式。如果要在出现重复值时保留插入顺序,可以执行以下操作:

from queue import PriorityQueue
from itertools import count

p = PriorityQueue()
index = count(0)

p.put((b.val, next(index), b))
p.put((c.val, next(index), c))

p.queue  # [(4, 0, Node(val=4)), (4, 1, Node(val=4))]

在heapq中也是如此,您可以很容易地将此行为 Package 在一个小函数中,这样就可以减少重复。

p = PriorityQueue()
index = count(0)

def put_priority(node):
    p.put((node.val, next(index), node))
vvppvyoh

vvppvyoh2#

PriorityQueue类不允许使用key参数,但是您可以将其子类化,以隐式地将每个项 Package 在一个比较器对象(这里称为_Wrapper)中。

from queue import PriorityQueue

class _Wrapper:
    def __init__(self, item, key):
        self.item = item
        self.key = key

    def __lt__(self, other):
        return self.key(self.item) < other.key(other.item)

    def __eq__(self, other):
        return self.key(self.item) == other.key(other.item)

class KeyPriorityQueue(PriorityQueue):
    def __init__(self, key):
        self.key = key
        super().__init__()

    def _get(self):
        wrapper = super()._get()
        return wrapper.item

    def _put(self, item):
        super()._put(_Wrapper(item, self.key))

用法

下面是颠倒元素顺序的优先级队列的示例。

key_pq = KeyPriorityQueue(key=lambda x: -x)

如果需要比较器而不是键,可以使用functools.cmp_to_key帮助器。

from functools import cmp_to_key

key_pq = KeyPriorityQueue(key=cmp_to_key(some_comparator))

相关问题