【问题标题】:How to make heapq evaluate the heap off of a specific attribute?如何让 heapq 评估特定属性的堆?
【发布时间】:2011-04-26 15:41:51
【问题描述】:

我希望拥有一堆对象,而不仅仅是数字。它们将具有堆可以排序的整数属性。在python中使用堆最简单的方法是heapq,但是在使用heapq时如何告诉它按特定属性排序呢?

【问题讨论】:

    标签: python data-structures heap


    【解决方案1】:

    很遗憾,您不能,尽管这是一个经常被要求的功能。

    一种选择是将(键,值)元组插入堆中。但是,如果值在比较时抛出异常(它们将在键之间存在关联的情况下进行比较),则这将不起作用。

    第二种选择是在类中定义一个__lt__(小于)方法,该方法将使用适当的属性来比较元素以进行排序。但是,如果对象是由另一个包创建的,或者如果您需要它们在程序的其他地方进行不同的比较,这可能是不可能的。

    第三种选择是使用blist 模块中的sortedlist 类(免责声明:我是作者)。 sortedlist 的构造函数采用 key 参数,该参数允许您指定返回元素排序键的函数,类似于 list.sortsortedkey 参数。

    【讨论】:

    • 我删除了我之前的评论,因为我与 blist 的问题可能是 PEBCAK(再次感谢您的模块),所以我只复制了之前评论的第一部分:总是可以定义一个通过子类化或封装使用__lt__ 的类。
    【解决方案2】:

    heapq 对对象进行排序的方式与 list.sort 相同,因此只需在您的类定义中定义一个方法 __cmp__(),它将自己与同一类的另一个实例进行比较:

    def __cmp__(self, other):
        return cmp(self.intAttribute, other.intAttribute)
    

    适用于 Python 2.x。

    在 3.x 中使用:

    def __lt__(self, other):
        return self.intAttribute < other.intAttribute
    

    【讨论】:

    • __cmp__ 在 3.x 中消失了。请改用__lt__
    • __lt__ 也适用于 Python 2,因此最好完全避免使用 __cmp__
    • 正如您可以告诉任何排序基于对象自然排序以外的标准进行排序(例如,cmpkey 用于sort),您应该能够告诉@ 987654333@ 根据不同的键进行排序。换句话说,您不必重新定义对象本身来更改保存它的特定数据结构;你应该能够告诉数据结构本身。这是heapq API 中缺少的一个值得注意的基本部分。
    • 大家有什么理由要求使用__lt__ 而不是__gt__?还是真的没关系?
    • 如果有时我想按这个属性排序,有时又按另一个属性排序怎么办?
    【解决方案3】:

    根据documentation的例子,你可以使用元组,它会按照元组的第一个元素排序:

    >>> h = []
    >>> heappush(h, (5, 'write code'))
    >>> heappush(h, (7, 'release product'))
    >>> heappush(h, (1, 'write spec'))
    >>> heappush(h, (3, 'create tests'))
    >>> heappop(h)
    (1, 'write spec')
    

    因此,如果您不想(或不能?)执行__cmp__ 方法,您可以在推送时手动提取排序键。

    请注意,如果一对元组中的第一个元素相等,则将比较后面的元素。如果这不是您想要的,您需要确保每个第一个元素都是唯一的。

    【讨论】:

    • "请注意,如果一对元组中的第一个元素相等,则将比较后面的元素。"你应该把它加粗,因为在文档中并不清楚。我假设给予相同的优先级它会返回我找到的第一个对象(没有充分的理由假设,所以这是我的错,我明白了)。
    • 好点。如果插入的元组是 (number, dict) 它不知道如何评估 dicts。
    • 如果你有一个像(some_value, dict) 这样的元组,你可以在堆中插入(some_value, counter, dict) 以打破与递增计数器的联系,以防some_value 等于2 个元组。
    • 这个例子对我不起作用。有什么建议? lst = [(18, [3, 3]), (26, [5, -1]), (20, [-2, 4])] heapq.heapify(lst)
    【解决方案4】:

    根据Official Document,解决方案是将条目存储为元组(请查看8.4.18.4.2 节)。

    例如,您的对象在 tuple 的格式中是这样的 (key, value_1, value_2)

    当你把对象(即tuples)放入heap时,它会取对象中的第一个属性(在这种情况下是key) 进行比较。如果出现平局,堆将使用下一个属性(即 value_1)等等。

    例如:

    import heapq
    
    heap = []
    heapq.heappush(heap, (0,'one', 1))
    heapq.heappush(heap, (1,'two', 11))
    heapq.heappush(heap, (1, 'two', 2))
    heapq.heappush(heap, (1, 'one', 3))
    heapq.heappush(heap, (1,'two', 3))
    heapq.heappush(heap, (1,'one', 4))
    heapq.heappush(heap, (1,'two', 5))
    heapq.heappush(heap, (1,'one', 1))
    
    show_tree(heap)
    

    输出:

                                          (0, 'one', 1)                                       
                    (1, 'one', 1)                                (1, 'one', 4)                
        (1, 'one', 3)         (1, 'two', 3)         (1, 'two', 2)         (1, 'two', 5)     
    (1, 'two', 11)
    

    关于在 python 中漂亮地打印一个堆(更新了链接):show_tree()

    【讨论】:

      【解决方案5】:

      你可以实现一个 heapdict。请注意使用 popitem() 来获取最低优先级的项目。

      import heapdict as hd
      import string
      import numpy as np
      
      h = hd.heapdict()
      keys = [char for char in string.ascii_lowercase[:10]]
      vals = [i for i in np.random.randint(0,10, 10)]
      for k,v in zip(keys,vals):
          h[k] = v
      for i in range(len(vals)):
          print h.popitem()
      

      【讨论】:

        【解决方案6】:

        我有同样的问题,但上述答案都没有到位,尽管有些答案很接近但不够详细。无论如何,我做了一些研究并尝试了这段代码,希望这对于下一个希望得到答案的人来说已经足够了:

        使用元组的问题是它只使用第一项,这不是很灵活。我想要类似于 c++ 中的 std::priority_queue 的东西,如下所示: std::priority_queue&lt;pair&lt;int, int&gt;, vector&lt;pair&lt;int, int&gt;&gt;, comparator&gt; pq; 我可以设计自己的比较器,这在现实世界的应用中更常见。

        希望下面的 sn-p 帮助: https://repl.it/@gururajks/EvenAccurateCylinders

        import heapq
        class PQNode:
        
            def __init__(self, key, value):
                self.key = key
                self.value = value
        
            # compares the second value
            def __lt__(self, other):
                return self.value < other.value
        
            def __str__(self):
                return str("{} : {}".format(self.key, self.value))
        
        input = [PQNode(1, 4), PQNode(7, 4), PQNode(6, 9), PQNode(2, 5)]
        hinput = []
        for item in input:
            heapq.heappush(hinput, item)
        
        while (hinput):
            print (heapq.heappop(hinput))
        

        【讨论】:

        • 我尝试了你的代码,它对我有用。我正在使用 python 3.6.5。我很好奇 heappush() 如何进行比较。这是由 PQNode 类中的特殊 _lt_() 方法本质上完成的吗?没有它,该程序肯定会因编译器消息而崩溃: Traceback (last most recent call last): File "heap_example.py", line 18, in heapq.heappush(hinput, item) TypeError: '
        【解决方案7】:

        我觉得最简单的方法是覆盖 heapq 模块现有的 cmp_lt 函数。一个简短的例子:

        import heapq
        
        # your custom function. Here, comparing tuples a and b based on their 2nd element
        def new_cmp_lt(self,a,b):
            return a[1]<b[1]
        
        #override the existing "cmp_lt" module function with your function
        heapq.cmp_lt=new_cmp_lt
        
        #Now use everything like normally used
        

        【讨论】:

          【解决方案8】:

          有一个模块叫做heaps。 Github 地址是https://github.com/gekco/heapy。您可以在类的实例化或从数组创建堆时应用自己的键/排序函数,这非常有用,因为这样可以节省您在每次执行操作时将其添加为参数。

          我想要列表的示例,元组最后一个位置的最小元素在堆顶部:

          >>> from heapy.heap import Heap 
          >>> a = [(3, 5, 10), (-5, 3, 8), (7, 8, 9), (-4, 0, 2)]
          >>> x = Heap.from_array(a, key=lambda t : t[-1])
          >>> x.length
          4
          >>> x.top()
          (-4, 0, 2)
          >>> x.insert((-1, 0, 1))
          >>> x.length
          5
          >>> x.top()
          (-1, 0, 1)
          >>> a
          [(3, 5, 10), (-5, 3, 8), (7, 8, 9), (-4, 0, 2)]
           
          

          【讨论】:

            猜你喜欢
            • 2013-01-14
            • 1970-01-01
            • 2015-07-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-25
            • 2019-08-06
            相关资源
            最近更新 更多