是否以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的py​​thon-dev消息:

做到这一点。 “Dict保持插入顺序”是裁决。 谢谢!

这仅仅意味着你可以依靠它。 如果Python的其他实现希望成为Python 3.7的一致性实现,则还必须提供插入有序字典。


在保留元素顺序的同时,Python 3.6字典实现如何比旧元素执行更好[2]?

基本上,通过保持两个数组。

  • 第一个数组dk_entries按照它们插入的顺序保存字典的条目(类型为PyDictKeyEntry )。 保留顺序是通过这是一个仅附加数组,其中总是插入新项目(插入顺序)来实现的。

  • 第二个是dk_indices ,它包含dk_entries数组的索引(即,表示dk_entries相应条目的位置的值)。 该数组充当散列表。 当一个键被散列时,它会导致存储在dk_indices一个索引,并且通过索引dk_entries来获取相应的条目。 由于只有索引被保留,此数组的类型取决于字典的整体大小(范围从类型int8_t1字节)至int32_t / int64_t4 / 8个字节)上32 / 64位版本)

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

    由于只存储了所需的条目(已插入的条目)和类型为intX_t (取决于字典大小的X )的稀疏数组,因此现在不是这种情况,所以保留2/3 * dk_size s full。 空白空间从PyDictKeyEntry类型PyDictKeyEntryintX_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必须保留插入顺序。

    链接地址: http://www.djcxy.com/p/5141.html

    上一篇: Are dictionaries ordered in Python 3.6+?

    下一篇: Build a Basic Python Iterator