简体   繁体   English

在 Python 中, heapq.heapify 不像 sorted 那样将 cmp 或 key 函数作为参数

[英]In Python, heapq.heapify doesn't take cmp or key functions as arguments like sorted does

I'm using python2.6.我正在使用 python2.6。 Is it available in higher version of python?它在更高版本的python中可用吗?
Else is there any other way I can maintain priority queues for list of objects of non-trivial classes?否则有没有其他方法可以为非平凡类的对象列表维护优先级队列? What I need is something like this我需要的是这样的

>>> l = [ ['a', 3], ['b', 1] ]
>>> def foo(x, y):
...   return x[1]-y[1]
>>> heap = heapify(l, cmp=foo)

Any suggestions ?有什么建议么 ?

The traditional solution is to store (priority, task) tuples on the heap:传统的解决方案是在堆上存储(优先级、任务)元组:

pq = [ ]
heappush(pq, (10, task1))
heappush(pq, (5, task2))
heappush(pq, (15, task3))
priority, task = heappop(pq)

This works fine as long as no two tasks have the same priority;只要没有两个任务具有相同的优先级,这就可以正常工作; otherwise, the tasks themselves are compared (which might not work at all in Python 3).否则,将比较任务本身(这在 Python 3 中可能根本不起作用)。

The regular docs give guidance on how to implement priority queues using heapq:常规文档提供了有关如何使用 heapq 实现优先级队列的指导:

http://docs.python.org/library/heapq.html#priority-queue-implementation-notes http://docs.python.org/library/heapq.html#priority-queue-implementation-notes

Just write an appropriate __lt__ method for the objects in the list so they sort correctly:只需为列表中的对象编写一个适当的__lt__方法,以便它们正确排序:

class FirstList(list):
    def __lt__(self, other):
        return self[0] < other[0]

lst = [ ['a', 3], ['b', 1] ]

lst = [FirstList(item) for item in lst]

Only __lt__ is needed by Python for sorting, though it's a good idea to define all of the comparisons or use functools.total_ordering . Python 只需要__lt__进行排序,尽管定义所有比较或使用functools.total_ordering是个好主意。

You can see that it is working by using two items with the same first value and different second values.您可以看到它通过使用具有相同第一个值和不同第二个值的两个项目来工作。 The two objects will swap places when you heapify no matter what the second values are because lst[0] < lst[1] will always be False .无论第二个值是什么,当您heapify时,这两个对象都会交换位置,因为lst[0] < lst[1]将始终为False If you need the heapify to be stable, you need a more complex comparison.如果需要heapify稳定,则需要进行更复杂的比较。

嗯,这太可怕了,你绝对不应该这样做……但是看起来heapq模块定义了一个cmp_lt函数,如果你真的想要一个自定义的比较函数,你可以修补它。

I don't know if this is better but it is like Raymond Hettinger's solution but the priority is determined from the object.我不知道这是否更好,但它就像 Raymond Hettinger 的解决方案,但优先级是由对象决定的。

Let this be your object and you want to sort by the the x attribute.让它成为您的对象,并且您希望按 x 属性进行排序。

class Item:                                 
    def __init__(self, x):
        self.x = x

Then have a function which applies the pairing然后有一个应用配对的功能

def create_pairs(items):
     return map(lambda item: (item.x, item), items)

Then apply the function to the lists as input into heapq.merge然后将该函数应用于列表作为 heapq.merge 的输入

list(heapq.merge(create_pairs([Item(1), Item(3)]), 
                 create_pairs([Item(2), Item(5)])))

Which gave me the following output这给了我以下输出

[(1, <__main__.Item instance at 0x2660cb0>),
 (2, <__main__.Item instance at 0x26c2830>),
 (3, <__main__.Item instance at 0x26c27e8>),
 (5, <__main__.Item instance at 0x26c2878>)]

With these Heap and HeapBy classes I tried to simplify the usage of heapq .通过这些HeapHeapBy类,我试图简化heapq的使用。 You can use HeapBy to pass a key sorting function.您可以使用HeapBy传递键排序功能。

Note that Raymond said that his solution won't work if priorities are repeated and the values are not sortable.请注意,Raymond 说如果重复优先级并且值不可排序,他的解决方案将不起作用。 That's why I added an example of HeapBy with a NonComparable class.这就是为什么我添加了一个带有NonComparable类的HeapBy示例。

I took the __lt__ idea from agf's solution .我从agf 的解决方案中获取__lt__想法。

Usage:用法:

# Use HeapBy with a lambda for sorting
max_heap = HeapBy(key=lambda x: -x)
max_heap.push(3)
max_heap.push(1)
max_heap.push(2)
assert max_heap.pop() == 3
assert max_heap.pop() == 2
assert max_heap.pop() == 1

# Use Heap as a convenience facade for heapq
min_heap = Heap()
min_heap.push(3)
min_heap.push(1)
min_heap.push(2)
assert min_heap.pop() == 1
assert min_heap.pop() == 2
assert min_heap.pop() == 3

# HeapBy also works with non-comparable objects.
# Note that I push a duplicated value
# to make sure heapq will not try to call __lt__ on it.

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

# Using non comparable values
max_heap = HeapBy(key=lambda x: -x.val)
max_heap.push(NonComparable(1))
max_heap.push(NonComparable(1))
max_heap.push(NonComparable(3))
max_heap.push(NonComparable(2))
assert max_heap.pop().val == 3
assert max_heap.pop().val == 2
assert max_heap.pop().val == 1
assert max_heap.pop().val == 1

Classes:课程:

import heapq

class Heap:
    """
    Convenience class for simplifying heapq usage
    """

    def __init__(self, array=None, heapify=True):
        if array:
            self.heap = array
            if heapify:
                heapq.heapify(self.heap)
        else:
            self.heap = []

    def push(self, x):
        heapq.heappush(self.heap, x)

    def pop(self):
        return heapq.heappop(self.heap)


class HeapBy(Heap):
    """
    Heap where you can specify a key function for sorting
    """

    # Item only uses the key function to sort elements,
    # just in case the values are not comparable
    class Item:
        def __init__(self, value, key):
            self.key = key
            self.value = value
        def __lt__(self, other):
            return self.key(self.value) < other.key(other.value)

    def __init__(self, key, array=None, heapify=True):
        super().__init__(array, heapify)
        self.key = key

    def push(self, x):
        super().push(self.Item(x, self.key))

    def pop(self):
        return super().pop().value

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM