字典是用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按照插入的顺序保存字典( PyDictKeyEntrytypes )的条目。 保留顺序是通过这样做来实现的,即只在最后插入新项目的插入数组(插入顺序)。

  • 第二个dk_indices保存dk_entries数组的索引(即,表示dk_entries相应条目的位置的值)。 这个数组作为哈希表。 当一个键被散列时,它将导致存储在dk_indices一个索引,并且通过索引dk_entries来获取相应的条目。 由于只保留了索引,因此这个数组的types取决于字典的整体大小(从64位版本的int8_t1字节)到int32_t / int64_tint8_t字节))。

在前面的实现中,必须分配一个types为PyDictKeyEntry和大小为dk_size的稀疏数组; 不幸的是,这也导致了很多空的空间,因为出于性能原因该数组不能超过2/3 * dk_size 。 (和空的空间仍然PyDictKeyEntry大小!)。

现在情况并非如此,因为只存储了所需的条目(已经插入的条目)和一个types为intX_t的稀疏数组(取决于字典大小),保留2/3 * dk_size s full。 空的空间从PyDictKeyEntrytypes改为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:)

这里有一个辩论。