听起来您已经找到了OrderedDict
在您的 2.7 版本之后某个时间点修复的错误。如果它不在任何实际发布的版本中,也许您可以忽略它。但除此之外,是的,你需要一个解决方法。
我建议,而不是 monkeypatching collections.OrderedDict
,您应该改用在 Python 2.4 或更高版本的文档中链接的Equivalent OrderedDict 配方(没有多余的)。如果不出意外,当有人走过来说“我需要在 2.6 上运行它,要移植多少工作”时,答案将是“少一点”……</p>
collections.OrderedDict
__del__
但还有两点:
重写所有内容以避免循环是一项巨大的努力。
您的字典中有循环这一事实是一个危险信号,表明您做错了什么(通常使用强引用作为缓存或反向指针),这可能会导致其他内存问题,并且可能会导致其他问题错误。因此,无论如何,这种努力可能是必要的。
你还没有解释你想要完成什么;我怀疑“确定性”的事情只是一个红鲱鱼(特别是因为dict
s 实际上是确定性的),所以最好的解决方案是s/OrderedDict/dict/g
.
但是如果确定性是必要的,你就不能依赖循环收集器,因为它不是确定性的,这意味着你的终结器排序等等都变得不确定。这也意味着你的内存使用是不确定的——你最终可能会得到一个程序,它在 99.999% 的时间里保持在你想要的内存范围内,但不是 100%;如果这些界限非常重要,那可能比每次都失败更糟糕。
同时,没有指定字典的迭代顺序,但实际上,CPython 和 PyPy 按哈希桶的顺序进行迭代,而不是值或键的 id(内存位置),以及 Jython 和 IronPython 所做的任何事情(他们可能正在使用一些具有不同行为的底层 Java 或 .NET 集合;我没有测试过),键的内存顺序不太可能是相关的。(你怎么能有效地基于类似的东西迭代一个哈希表?)你可能对使用id
for的对象进行测试感到困惑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))
如果您使用 CPython 2.7、CPython 3.2 和 PyPy 1.9 重复运行它,键将始终按 0、1、2 的顺序迭代。id
列也可能每次都相同(这取决于您的平台),但您可以以多种方式修复它——以不同的顺序插入,反转值的顺序,使用字符串值而不是整数,将值分配给变量,然后插入这些变量而不是文字等。玩够了,您可以获得id
列的所有可能顺序,但键仍然每次都以相同的顺序迭代。
迭代的顺序是不可预测的,因为要预测它,您需要转换hash(k)
为存储桶索引的函数,这取决于您无法从 Python 访问的信息。即使它只是hash(k) % self._table_size
,除非它_table_size
暴露给 Python 接口,否则它也无济于事。(这是插入和删除序列的复杂函数,原则上可以计算出来,但实际上尝试起来很愚蠢。)
但它是确定性的;如果每次都以相同的顺序插入和删除相同的键,则每次的迭代顺序都是相同的。