有没有理由不使用有序的字典?
我指的是collections
模块中的OrderedDict 。
如果它具有可订购的附加function,我认为可能经常不需要,但即使如此,是否有任何缺点? 它慢吗? 它是否缺less任何function? 我没有看到任何遗漏的方法。
总之,为什么我不应该总是用这个而不是一个正常的字典呢?
OrderedDict
是dict
一个子类,需要更多的内存来跟踪键的添加顺序。 这不是微不足道的。 在实现中添加了第二个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一个空的OrderedDict
( d = 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。