是否以Python 3.6+订购字典?
字典在Python 3.6(至少在CPython实现下)与以前的版本不同。 这似乎是一个很大的变化,但这只是文档中的一小段。 它被描述为CPython实现细节而不是语言特性,但也意味着这可能在未来成为标准。
新的词典实现如何在保持元素顺序的同时比旧的更好?
以下是文档中的文字:
dict()
现在使用由PyPy开创的“紧凑”表示。 与Python 3.5相比,新dict()的内存使用量减少了20%到25%。 PEP 468(保留函数中的** kwargs的顺序)由此实现。 这个新实现的顺序保留方面被认为是一个实现细节,不应该被依赖(这可能会在将来发生变化,但是希望在改变语言规范之前在几种版本中使用这种新的dict实现语言为所有当前和将来的Python实现强制实现顺序保留语义;这也有助于保持随机迭代顺序仍然有效的老版本语言(例如Python 3.5)的向后兼容性。 (由INADA Naoki在第27350期发表。Idea最初由Raymond Hettinger提出。)
2017年12月更新:为Python 3.7保证了dict
的保留插入顺序
是否以Python 3.6+订购字典?
他们插入有序 [1] 。 从Python 3.6开始,对于Python的CPython实现,字典记住插入项目的顺序。 这被认为是Python 3.6中的一个实现细节; 如果您想要在其他Python实现(以及其他有序行为[1] )中保证的插入顺序,则需要使用OrderedDict
。
从Python 3.7开始 ,这不再是一个实现细节,而是成为一种语言功能。 从GvR的python-dev消息:
做到这一点。 “Dict保持插入顺序”是裁决。 谢谢!
这仅仅意味着你可以依靠它。 如果Python的其他实现希望成为Python 3.7的一致性实现,则还必须提供插入有序字典。
在保留元素顺序的同时,Python 3.6
字典实现如何比旧元素执行更好[2]?
基本上,通过保持两个数组。
第一个数组dk_entries
按照它们插入的顺序保存字典的条目(类型为PyDictKeyEntry
)。 保留顺序是通过这是一个仅附加数组,其中总是插入新项目(插入顺序)来实现的。
第二个是dk_indices
,它包含dk_entries
数组的索引(即,表示dk_entries
相应条目的位置的值)。 该数组充当散列表。 当一个键被散列时,它会导致存储在dk_indices
一个索引,并且通过索引dk_entries
来获取相应的条目。 由于只有索引被保留,此数组的类型取决于字典的整体大小(范围从类型int8_t
( 1
字节)至int32_t
/ int64_t
( 4
/ 8
个字节)上32
/ 64
位版本)
在前面的实现中,必须分配一个类型为PyDictKeyEntry
和大小为dk_size
的稀疏数组; 不幸的是,它还导致了很多空的空间,因为出于性能原因该数组不能超过2/3 * dk_size
。 (并且空的空间仍然有PyDictKeyEntry
大小!)。
由于只存储了所需的条目(已插入的条目)和类型为intX_t
(取决于字典大小的X
)的稀疏数组,因此现在不是这种情况,所以保留2/3 * dk_size
s full。 空白空间从PyDictKeyEntry
类型PyDictKeyEntry
为intX_t
。
所以,显然,创建一个类型为PyDictKeyEntry
的稀疏数组要比存储int
的稀疏数组需要更多的内存。
如果您感兴趣的话,您可以在Python-Dev上看到关于此功能的完整对话,这是一个很好的阅读。
在由Raymond Hettinger提出的最初提案中,可以看到所使用的数据结构的可视化,它抓住了这个想法的要点。
例如,字典:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
目前存储为:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
相反,数据应按以下方式组织:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
正如您现在所看到的,在原始提案中,大量空间基本上是空的,以减少冲突并加快查找速度。 采用这种新方法,您可以通过在索引中移动真正需要的稀疏性来减少所需的内存。
[1]:我说“有序插入”而不是“有序”,因为OrderedDict的存在,“有序”表明dict
对象没有提供进一步的行为。 OrderedDicts是可逆的,提供顺序敏感的方法,并且主要提供顺序敏感的相等测试( ==
, !=
)。 dict
目前不提供任何这些行为/方法。
[2]:新的字典实现通过更紧凑地设计来实现更好的记忆 ; 这是这里的主要优点。 速度方面,差别并不那么激烈,有些地方新字典可能会引入轻微回归(例如键盘查找),而在其他情况下(迭代和调整大小时会想到)应该存在性能提升。
总的来说,由于引入了紧凑性,字典的性能,特别是在现实生活中的情况得到改善。
下面回答原来的第一个问题:
我应该在Python 3.6中使用dict
还是OrderedDict
?
我认为文档中的这句话实际上足以回答你的问题
这个新实现的顺序保留方面被认为是一个实现细节,不应该被依赖
dict
不是明确意味着是一个有序的集合,所以如果你想保持一致并且不依赖于新实现的副作用,你应该使用OrderedDict
。
让你的代码未来证明:)
这里有一场辩论。
编辑: Python 3.7将保持这个功能看
更新:Guido van Rossum在邮件列表中宣布,在所有Python实现中,Python 3.7 dict
必须保留插入顺序。