collections
模块的OrderedDict,它是一个有序字典。如果它具有可排序的附加功能,我意识到这可能经常不必要,但即使如此,它是否存在任何缺点?它是否更慢?它是否缺少任何功能?我没有看到任何缺失的方法。
简而言之,为什么我不应该总是使用它而不是普通字典?
collections
模块的OrderedDict,它是一个有序字典。OrderedDict
是dict
的一个子类,需要更多的内存来跟踪添加键的顺序。这不是微不足道的。实现在底层添加了第二个dict
,以及所有键的双向链表(记住顺序的部分),和一堆弱引用代理。它并不慢很多,但至少比使用普通的dict
增加了一倍的内存。
但如果适当的话,请使用它!这就是为什么它存在的原因:-)
基本字典只是将键映射到值的普通字典,根本不是"有序"的。当添加一对<key, value>
时,key
被追加到列表中。列表是记住顺序的部分。
但是,如果这是一个Python列表,删除键将重复O(n)
次:在列表中查找键需要O(n)
时间,从列表中删除键也需要O(n)
时间。
所以它是一个双向链接列表。这使得删除一个键成为常数(O(1)
)时间。但是我们仍然需要找到属于键的双向链接列表节点。要使该操作的时间复杂度也为O(1)
,第二个-隐藏的-字典将键映射到双向链接列表中的节点。
因此,添加一个新的<key, value>
对需要将该对添加到基本字典中,创建一个新的双向链接列表节点来保存键,将该新节点附加到双向链接列表中,并将键映射到该新节点中的隐藏字典中。它是比普通的dict
多了一点工作量,但总体上仍然是O(1)
(预期情况下)的时间复杂度。
O(1)
:使用隐藏的字典来查找键的双向链表节点,从链表中删除该节点,并从两个字典中删除该键。Lib/collections/__init__.py
中找到该代码。 - Tim Petersimport this
,然后按回车键。编写这个程序的人就是回答这个问题的人。 - Games Brainiac多线程
如果您的字典在没有锁定的情况下从多个线程访问,特别是作为同步点。
原始字典操作是原子操作的,而Python中扩展的任何类型都不是。
事实上,我甚至不能确定OrderedDict是否是线程安全的(没有锁定),尽管我不能排除它非常小心地编码并满足可重入性的定义的可能性。
小恶魔
如果您创建了大量这些字典,则会增加内存使用量。
如果您的所有代码都是修改这些字典,那么会增加CPU使用率。
为什么我不能一直使用这个而不是普通的字典
在Python 2.7中,正常的OrderedDict
用法会创建引用循环。因此,任何使用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']
OrderedDict
(d = collections.OrderedDict()
)并且没有添加任何内容,或者您明确尝试通过调用clear
方法(在del d
之前d.clear()
)来清除它,您仍将获得一个自引用列表:gc: collectable <list 0000000003ABBA08>
0 [[...], [...], None]
自从这个提交移除了__del__
方法以防止OrderedDict
引起不可回收循环,这似乎一直是这种情况。正如该提交的更改日志中所述:
问题#9825:从collections.OrderedDict的定义中删除了__del__。 这可以防止用户创建的自引用有序字典变为永久无法回收的GC垃圾。缺点是 移除__del__意味着内部双向链接列表必须等待GC收集,而不是在refcnt降至零时立即释放内存。
问题#9825:在collections.OrderedDict的定义中使用__del__使得用户可以创建自引用的有序字典,这些字典成为永久无法收集的垃圾。重新采用Py3.1的方法,使用弱引用代理,以便首先不会创建引用循环。
for key in sorted(dictvar): print (key, dictvar[key])
。OrderedDict保留插入的顺序,而不是键的顺序。 - PaulMcG