【问题标题】:Intercepting heapq拦截 heapq
【发布时间】:2014-06-29 05:19:30
【问题描述】:

我想使用 Python 的 heapq 模块。但是,我需要跟踪每个值设置为哪个索引。

所以我写了

class heap(list):       
    def __init__(self,xs):
        super(heap,self).__init__(xs)
        self._index_table = {x:i for i,x in enumerate(self)}

    def __setitem__(self,i,v):
        print(i,v)
        super(heap,self).__setitem__(i,v)
        self._index_table[v] = i

    def append(self,x):
        super(heap,self).append(x)
        self._index_table[x] = len(self)-1

from heapq import heapify, heappush, heappop, _siftdown, _siftup

h = heap([4,3,2,1])
heapify(h)
heappush(h,12)
print(h)
print(h._index_table)

这会打印出来

[1, 3, 2, 4, 12]
{1: 3, 2: 2, 3: 1, 4: 0}

heapifyheappush 修改了我列表中的条目,以规避我试图捕获所有作业的尝试。

为什么会这样?有没有解决的办法?还有一种方法可以让我使用heapq 模块并仍然跟踪每个值对应的索引吗?

编辑:

看起来我的代码有一个 heapify(h) 行,而我在原始帖子中没有。解决了这个问题。

【问题讨论】:

  • 我没有具体的建议,但看起来heapifyheappush 正在调用列表修改方法,但您没有重载。您是否尝试过查找 heapq 模块的源代码以查看它调用了哪些列表方法?
  • @SamMussmann heapq.py 看起来主要是使用简单的赋值和追加。但实际上我只是注意到它在底部导入了一个C implementation。 python 代码本身看起来应该和我的代码配合得很好......

标签: python heap


【解决方案1】:

在阅读heapq 源代码时,我注意到@math4tots 所做的是它正在导入一个C 实现。所以我运行了以下代码来证明它是使用 python 源代码(它会从 list 调用可重载的方法),还是使用将编译方法用于列表的 C 实现:

>>> class heap(list):        
...     def __init__(self,xs):
...         super(heap,self).__init__(xs)
...         self._index_table = {x:i for i,x in enumerate(self)}
...     
...     def __setitem__(self,i,v):
...         print("SETITEM")
...         print(i,v)
...         super(heap,self).__setitem__(i,v)
...         self._index_table[v] = i
...     
...     def append(self,x):
...         print("APPEND")
...         super(heap,self).append(x)
...         self._index_table[x] = len(self)-1
... 
>>> 
>>> 
>>> h = heap([4,3,2,1])
>>> heapify(h)
>>> h
[1, 3, 2, 4]
>>> h._index_table
{1: 3, 2: 2, 3: 1, 4: 0}
>>> heappush(h,42)
>>> h
[1, 3, 2, 4, 42]
>>> h._index_table
{1: 3, 2: 2, 3: 1, 4: 0}

它不打印单个字符串……这意味着它不使用我们正在查看的 python 源代码,但绝对是编译版本。

所以你的代码不太可能按原样工作......

阅读 heapq module 的 C 源代码证明我们是对的:_siftup 函数正在使用 PyList_SET_ITEM() 从列表中获取值,从而覆盖任何重载方法的尝试。

虽然,所有的希望都没有失去,继续阅读 C 源代码,让我发现实际上 C 模块 does not export_sitf* 函数实现了 heapq 算法。因此,我仔细检查了以下内容:

>>> heapq.heapify
<built-in function heapify>
>>> heapq._siftup
<function _siftup at 0x10b36ab00>

这证明我是对的!

因此,您始终可以使用 heapq 模块中未被 C 代码隐藏的 _siftup()_siftdown() 函数重新实现大约几行长的 heapify()heappush() 函数。

所以这里将重新实现它:

import heapq

class HeapQueue(list):
    def __init__(self,xs):
        super(HeapQueue,self).__init__(xs)
        self._index_table = {x:i for i,x in enumerate(self)}
    def __setitem__(self,i,v):
        super(HeapQueue,self).__setitem__(i,v)
        self._index_table[v] = i
    def append(self,x):
        super(HeapQueue,self).append(x)
        self._index_table[x] = len(self)-1
    def push(self, x):
        self.append(x)
        heapq._siftdown(self, 0, len(self)-1)
    def heapify(self):
        n = len(self)
        for i in reversed(range(n//2)):
            heapq._siftup(self, i)

结果:

>>> h = HeapQueue([4,3,2,1])
>>> print(h._index_table)
{1: 3, 2: 2, 3: 1, 4: 0}
>>> h.heapify()
>>> print(h)
[1, 3, 2, 4]
>>> print(h._index_table)
{1: 0, 2: 2, 3: 1, 4: 3}
>>> h.push(42)
>>> print(h._index_table)
{1: 0, 2: 2, 3: 1, 4: 3, 42: 4}
>>> print(h)
[1, 3, 2, 4, 42]
>>> 

我的猜测是,您不想保留 heapify() 方法,而是将其作为构造函数的一部分,并为您自己的 Heap Queue 类考虑一个更好的接口。我只是将其作为该想法的概念证明。

HTH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-22
    • 2013-06-25
    • 2018-08-09
    • 2014-03-10
    • 2023-03-13
    • 1970-01-01
    • 2018-12-25
    • 2013-03-30
    相关资源
    最近更新 更多