字典是用Python 3.6+订购的
字典在Python 3.6中(至less在CPython实现下)与以前的版本不同。 这似乎是一个实质性的变化,但这只是文档中的一小段。 它被描述为CPython的实现细节而不是语言特征,但也意味着这可能在未来成为标准。
新的字典实现如何在保持元素顺序的同时比旧的更好?
以下是文档中的文字:
dict()
现在使用由PyPy开创的“紧凑”表示。 新的dict()的内存使用比Python 3.5要小20%到25%。 PEP 468 (保留函数中的** kwargs的顺序)由此实现。 这个新实现的顺序保留方面被认为是一个实现细节,不应该被依赖(这可能会在将来发生变化,但是希望在改变语言规范之前在几个版本中使用这个新的dict实现语言为所有当前和未来的Python实现指定保留顺序的语义;这也有助于保持随机迭代顺序仍然有效的老版本语言的向后兼容性,例如Python 3.5)。 (由INADA Naoki提供, 刊登在27350号文件中, 最初由Raymond Hettinger提出 。
编辑:Raymond Hettinger 最近在推特上说 :“[有了dict
命令]非常方便,对[Python] 3.7的保证几乎是不可避免的”。
编辑2:如何CPython 3.6 dict
的两个例子并不总是显示在插入顺序, 与pprint
和IPython的pprint
。
在保持元素顺序的同时,Python
3.6
字典实现如何比旧元素执行更好?
本质上,通过保持两个数组 。
-
第一个数组
dk_entries
按照插入的顺序保存字典(PyDictKeyEntry
types )的条目。 保留顺序是通过这样做来实现的,即只在最后插入新项目的插入数组(插入顺序)。 -
第二个
dk_indices
保存dk_entries
数组的索引(即,表示dk_entries
相应条目的位置的值)。 这个数组作为哈希表。 当一个键被散列时,它将导致存储在dk_indices
一个索引,并且通过索引dk_entries
来获取相应的条目。 由于只保留了索引,因此这个数组的types取决于字典的整体大小(从64
位版本的int8_t
(1
字节)到int32_t
/int64_t
(int8_t
字节))。
在前面的实现中,必须分配一个types为PyDictKeyEntry
和大小为dk_size
的稀疏数组; 不幸的是,这也导致了很多空的空间,因为出于性能原因该数组不能超过2/3 * dk_size
。 (和空的空间仍然有PyDictKeyEntry
大小!)。
现在情况并非如此,因为只存储了所需的条目(已经插入的条目)和一个types为intX_t
的稀疏数组(取决于字典大小),保留2/3 * dk_size
s full。 空的空间从PyDictKeyEntry
types改为intX_t
。
所以,显然,创build一个types为PyDictKeyEntry
的稀疏数组要比存储int
的稀疏数组需要更多的内存。
如果感兴趣的话,你可以在Python-Dev上看到关于这个特性的完整对话,这是一个很好的阅读。
在Raymond Hettinger最初提出的build议中 ,可以看到所使用的数据结构的可视化,它抓住了这个想法的要点。
例如,字典:
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']]
正如你现在所看到的,在原来的build议中,大量的空间基本上是空的,以减less冲突并加快查找速度。 采用这种新方法,可以通过在索引中移动实际需要的稀疏性来减less所需的内存。
*新的字典实现通过更紧凑的devise执行更好的内存 ; 这是主要的好处。 速度方面,差别并不那么激烈,有些地方可能会引入轻微的回归( 例如键盘查找 ),而在另外一些地方(迭代和resize时会想到)性能提升应该存在。
总的来说,字典的性能,特别是在现实生活中,由于引入了紧凑性而得到改善。
你应该依赖它还是使用它?
没有! 任何以实现细节为特征的东西(如文档中所述)应该是你不应该依赖的东西。 如果你这样做,将会是你的错,如果你以前的版本/不同的实现中写入了代码,并且/或者在以后的版本中停止工作。
不需要Python的不同实现来使字典sorting,而是只需要支持需要的映射(值得注意的例子是PEP 520:保留类属性定义顺序和PEP 468:保留关键字参数顺序 )
如果你想编写保留顺序的代码,并且希望在以前的版本/不同的实现中不要中断,你应该使用OrderedDict
。 另外OrderedDict
很可能最终会成为新的dict
实现的一个简单封装。
注意:在某些时候,这可能会从实现细节提升到function(正如RDH推荐的那样 )。 如果发生这种情况,当然不适用。
下面回答原来的第一个问题:
我应该在Python 3.6中使用
dict
还是OrderedDict
?
我认为从文档中的这句话实际上足以回答你的问题
这个新实现的顺序保留方面被认为是一个实现细节,不应该被依赖
dict
不是明确意味着是一个有序的集合,所以如果你想保持一致,而不是依靠新的实现的副作用,你应该坚持OrderedDict
。
让你的代码未来的certificate:)
这里有一个辩论。