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

时间:2011-10-18 06:11:39

标签: python

我正在使用python2.6。它是否可以在更高版本的python中使用? 还有其他方法可以维护非平凡类对象列表的优先级队列吗? 我需要的是这样的东西

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

有什么建议吗?

5 个答案:

答案 0 :(得分:29)

传统的解决方案是在堆上存储(优先级,任务)元组:

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

只要没有两个任务具有相同的优先级,这就可以正常工作;否则,比较任务本身(在Python 3中可能根本不起作用)。

常规文档提供了有关如何使用heapq实现优先级队列的指导:

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

答案 1 :(得分:20)

只需为列表中的对象编写适当的__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]

Python只需要__lt__进行排序,但定义所有比较或使用functools.total_ordering是个好主意。

通过使用具有相同第一个值和不同第二个值的两个项目,您可以看到它正在工作。无论第二个值是什么,这两个对象都会在您heapify时交换位置,因为lst[0] < lst[1]始终为False。如果您需要heapify保持稳定,则需要进行更复杂的比较。

答案 2 :(得分:2)

嗯,这太糟糕了,你肯定不应该这样做......但它看起来像heapq模块defines a cmp_lt function,如果你真的想要一个自定义比较功能,你可以修补它。

答案 3 :(得分:1)

我不知道这是否更好,但它就像Raymond Hettinger的解决方案,但优先级取决于对象。

让它成为您的对象,并且您希望按x属性进行排序。

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

然后有一个应用配对的功能

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

然后将该函数应用于列表作为heapq.merge

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

这给了我以下输出

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

答案 4 :(得分:1)

使用这些HeapHeapBy类,我试图简化heapq的用法。您可以使用HeapBy来传递键排序功能。

请注意,雷蒙德说,如果重复优先级并且值不可排序,那么他的solution将不会起作用。因此,我添加了一个HeapBy类的NonComparable示例。

我从agf's solution采纳了__lt__的想法。

用法:

# 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

课程:

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