Python订购垃圾收藏字典?

时间:2012-10-01 03:01:50

标签: python garbage-collection ordereddictionary

我希望我的Python程序具有确定性,因此我在整个代码中广泛使用OrderedDicts。不幸的是,在今天调试内存泄漏时,我发现OrderedDicts有一个自定义的__del__方法,只要有一个循环就会让它们无法收集。非常不幸的是,文档中没有关于此的警告。

那我该怎么办? Python标准库中是否有与gc一起使用的确定性字典?我真的很讨厌不得不自己动手,尤其是像这样的愚蠢的一行功能。

另外,这是我应该提交错误报告的吗?我不熟悉Python库的程序,以及他们认为是什么错误。

编辑:It appears that this is a known bug that was fixed back in 2010. I must have somehow gotten a really old version of 2.7 installed.我想最好的方法就是只包含一个猴子补丁,以防用户碰巧像我一样运行破碎的版本。

3 个答案:

答案 0 :(得分:2)

如果__del__方法存在问题,请将其删除:

>>> import collections
>>> del collections.OrderedDict.__del__

您将获得在参考周期中使用OrderedDicts的能力。删除后,OrderedDict将立即释放所有资源。

答案 1 :(得分:1)

听起来你已经跟踪了OrderedDict中的一个错误,该错误在2.7版本之后的某个时刻被修复了。如果它不是任何实际发布的版本,也许你可以忽略它。但除此之外,是的,你需要一个解决方法。

我建议您使用Equivalent OrderedDict recipe that runs on Python 2.4 or laterthe documentation链接collections.OrderedDict而不是monkeypatching collections.OrderedDict,而不是__del__ dict }})。如果不出意外的话,当有人出现时说“我需要在2.6上运行它,它需要多少工作”,答案将“少一点”...

但还有两点:

  重写一切以避免周期是一项巨大的努力。

你的字典中有循环的事实是一个红旗,你做错了(通常使用强引用缓存或后向指针),这可能会导致其他内存问题,可能还有其他的错误。因此,无论如何,这种努力可能都是必要的。

你还没有解释你想要完成的事情;我怀疑“确定性”的东西只是一个红色的鲱鱼(特别是因为s/OrderedDict/dict/g实际上是确定性的),所以最好的解决方案是id

但是如果确定性是必要的,你就不能依赖于循环收集器,因为它不是确定性的,这意味着你的终结器排序等都变得不确定。这也意味着你的内存使用是不确定的 - 你可能最终得到的程序在99.999%的时间内保持在你想要的内存范围内,但不是100%;如果这些界限至关重要,那可能比每次都失败更糟糕。

同时,没有指定字典的迭代顺序,但实际上,CPython和PyPy按照散列桶的顺序迭代,而不是值或键的id(内存位置),以及任何Jython和IronPython做(他们可能正在使用一些具有不同行为的底层Java或.NET集合;我还没有测试过),密钥的内存顺序不太可能是相关的。 (你怎么能基于类似的东西有效地迭代哈希表?)你可能会因为使用hash d={} d[0] = 0 d[1] = 1 d[2] = 2 for k in d: print(k, d[k], id(k), id(d[k]), hash(k)) 的对象进行测试而感到困惑,但是大多数对象都基于值进行哈希。

例如,采取这个简单的程序:

id

如果使用CPython 2.7,CPython 3.2和PyPy 1.9重复运行,则密钥将始终按0,1,2顺序迭代。id列可能每次都相同(取决于您的平台),但您可以通过多种方式修复它 - 以不同的顺序插入,反转值的顺序,使用字符串值而不是整数,将值分配给变量然后插入那些变量而不是文字等。用它充分玩,你可以得到hash(k)列的所有可能的顺序,但每次仍然按相同的顺序迭代键。

迭代的顺序不是可预测的,因为为了预测它,您需要将hash(k) % self._table_size转换为存储桶索引的功能,这取决于您无法访问的信息。蟒蛇。即使它只是_table_size,除非{{1}}暴露给Python接口,否则它没有帮助。 (这是插入和删除序列的复杂函数,原则上可以计算,但在实践中尝试是愚蠢的。)

但它是 deterministic ;如果每次都以相同的顺序插入和删除相同的键,则每次迭代顺序都是相同的。

答案 2 :(得分:0)

请注意the fix made in Python 2.7消除__del__方法并因此阻止它们无法收集不幸意味着每次使用OrderedDict(即使是空方法)都会导致参考周期必须是垃圾收集。有关详细信息,请参阅this answer