有没有理由不使用有序的字典?

我指的是collections模块中的OrderedDict 。

如果它具有可订购的附加function,我认为可能经常不需要,但即使如此,是否有任何缺点? 它慢吗? 它是否缺less任何function? 我没有看到任何遗漏的方法。

总之,为什么我不应该总是用这个而不是一个正常的字典呢?

OrderedDictdict一个子类,需要更多的内存来跟踪键的添加顺序。 这不是微不足道的。 在实现中添加了第二个dict ,以及所有键的双向链接列表(这是logging顺序的部分)以及一堆weakref代理。 这不是慢,但至less使用一个简单的dict双倍的记忆。

但如果合适的话,就用它吧! 这就是为什么它在那里:-)

怎么运行的

基本字典只是一个普通的字典映射键值 – 它不是“有序”的。 当添加<key, value>对时,该key被附加到列表中。 该清单是logging订单的部分。

但是,如果这是一个Python列表, 删除一个键将花费O(n)两倍的时间: O(n)时间在列表中find键, O(n)时间从列表中删除键。

所以这是一个双向链表。 这使得删除一个关键常量( O(1) )的时间。 但是我们仍然需要find属于这个键的双链表节点。 为了使操作O(1)也是这样,第二个隐藏的词典将键映射到双链表中的节点。

因此,添加一个新的<key, value>对需要将该对添加到基本字典中,创build一个新的双向链表列表来保存该键,将该新节点附加到双向链表中,并将键映射到新的隐藏字典中的节点。 有点过了两倍的工作,但仍然O(1) (预期的情况下)整体时间。

类似的,删除一个已经存在的关键字也有点超过两倍于O(1)总体预期时间:使用隐藏字典find关键字的双向链表节点,从列表中删除该节点,并删除关键字从两个字。

等等,这是非常有效的。

multithreading

如果你的字典是从多个线程访问而没有锁,特别是作为一个同步点。

vanilla dict操作是primefaces操作,Python中扩展的任何types都不是。

事实上,我甚至不确定OrderedDict是线程安全的(没有锁),尽pipe我不能打折它被仔细编码并满足重入定义的可能性。

较小的恶魔

内存使用情况如果您创build吨这些字典

CPU的使用,如果你所有的代码确实是这些字典

为什么我不应该总是使用这个而不是一个正常的字典

在Python 2.7中, 正常的OrderedDict用法会创build引用循环 。 所以任何使用OrderedDict需要启用垃圾回收器来释放内存。 是的,默认情况下,垃圾收集器在cPython中处于打开状态,但禁用垃圾收集器有其用处 。

例如用cPython 2.7.14

 from __future__ import print_function import collections import gc if __name__ == '__main__': d = collections.OrderedDict([('key', 'val')]) gc.collect() del d gc.set_debug(gc.DEBUG_LEAK) gc.collect() for i, obj in enumerate(gc.garbage): print(i, obj) 

输出

 gc: collectable <list 00000000033E7908> gc: collectable <list 000000000331EC88> 0 [[[...], [...], 'key'], [[...], [...], 'key'], None] 1 [[[...], [...], None], [[...], [...], None], 'key'] 

即使你只是创build一个空的OrderedDictd = collections.OrderedDict() )而不添加任何东西,或者你明确地尝试通过调用clear方法( d.clear()del d之前)来清理它,你仍然会得到一个自引用列表:

 gc: collectable <list 0000000003ABBA08> 0 [[...], [...], None] 

这似乎是这种情况,因为这个提交删除了__del__方法,以防止潜在OrderedDict导致无法收集的周期,这可以说是更糟糕的。 正如该提交的更改日志中所述:

问题#9825 :从collections.OrderedDict的定义中删除__del__。 这可以防止用户创build的自引用sorting字典变成永久不可收集的GC垃圾。 缺点是删除__del__意味着内部双向链表必须等待GC收集,而不是立即释放内存,当refcnt下降到零。


请注意,在Python 3中,针对相同问题的修复方式有所不同,并使用weakref代理来避免循环:

问题#9825:在collections.OrderedDict的定义中使用__del__使得用户可以创build自引用的有序字典,这些字典变成永久不可收集的GC垃圾。 恢复了使用weakref代理的Py3.1方法,以便引用循环从不首先创build。