是否在Python 3.6+中订购了字典?

时间:2016-10-11 14:59:23

标签: python python-3.x dictionary python-internals python-3.6

字典在Python 3.6中排序(至少在CPython实现下),与之前的版本不同。这似乎是一个重大变化,但它只是documentation中的一个短段。它被描述为CPython实现细节而不是语言特性,但也暗示这可能成为未来的标准。

在保留元素顺序的同时,新字典实现如何比旧字典实现更好?

以下是文档中的文字:

  

dict()现在使用“紧凑”表示pioneered by PyPy。与Python 3.5相比,新dict()的内存使用量减少了20%到25%。 PEP 468(保留函数中** kwargs的顺序。)由此实现。这个新实现的顺序保留方面被认为是一个实现细节,不应该依赖(这可能会在未来发生变化,但是在更改语言规范之前,希望在几种版本的语言中使用这个新的dict实现为所有当前和未来的Python实现强制命令保留语义;这也有助于保持与随机迭代顺序仍然有效的语言的旧版本的向后兼容性,例如Python 3.5)。 (由INADA Naoki提供,issue 27350。想法originally suggested by Raymond Hettinger。)

2017年12月更新:对于Python 3.7,dict保留插入顺序为guaranteed

5 个答案:

答案 0 :(得分:349)

  

是否在Python 3.6 +中订购了词典?

插入 [1] 。从Python 3.6开始,对于Python的CPython实现,字典记住插入项目的顺序这被认为是Python 3.6中的实现细节;如果您想要在其他Python实现中插入保证的插入顺序(以及其他有序行为 [1] ,则需要使用OrderedDict功能)。

从Python 3.7开始,这不再是一个实现细节,而是成为一种语言功能。 From a python-dev message by GvR

  

这样做。 " Dict保持插入顺序"是裁决。谢谢!

这只是意味着你可以依赖它。如果Python希望成为Python 3.7的一致实现,那么Python的其他实现也必须提供插入有序字典。

  

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

基本上,通过保留两个数组

  • 第一个数组dk_entries按照插入顺序保存字典的条目(of type PyDictKeyEntry)。保留顺序是通过仅作为附加数组来实现的,其中新项目总是在末尾插入(插入顺序)。

  • 第二个dk_indices包含dk_entries数组的索引(即表示dk_entries中相应条目位置的值)。此数组充当哈希表。当一个密钥被散列时,它会导致存储在dk_indices中的一个索引,并通过索引dk_entries来获取相应的条目。由于只保留索引,因此该数组的类型取决于字典的整体大小(范围从int8_t1字节)到int32_t / int64_t({ 4 / 8位构建上的{1}} / 32字节)

在前面的实现中,必须分配类型为64和大小为PyDictKeyEntry的稀疏数组;不幸的是,它也导致了很多空白空间,因为该数组不允许超过dk_sizefor performance reasons。 (空格仍然大小为2/3 * dk_size!)。

现在不是这种情况,因为只存储了必需的条目(已插入的条目)和类型为PyDictKeyEntry的稀疏数组(intX_t,具体取决于字典大小)X已保存完整。空格已从类型2/3 * dk_size更改为PyDictKeyEntry

因此,显然,创建类型为intX_t的稀疏数组要比存储PyDictKeyEntry的稀疏数组要多得多。

如果感兴趣,您可以看到有关此功能的完整对话on Python-Dev,这是一个很好的阅读。

In the original proposal made by Raymond Hettinger,可以看到所使用的数据结构的可视化,它捕获了这个想法的要点。

  

例如,字典:

int
     

目前存储为:

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
     

相反,数据应按如下方式组织:

entries = [['--', '--', '--'],
           [-8522787127447073495, 'barry', 'green'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           [-9092791511155847987, 'timmy', 'red'],
           ['--', '--', '--'],
           [-6480567542315338377, 'guido', 'blue']]

正如您现在可以看到的那样,在原始提案中,大量空间基本上是空的,以减少碰撞并使查找更快。使用新方法,您可以通过在索引中移动实际需要的稀疏性来减少所需的内存。

<子> [1]:我说'#34;插入顺序&#34;而不是&#34;有序&#34;因为,随着OrderedDict的存在,&#34;有序&#34;表示indices = [None, 1, None, None, None, 0, None, 2] entries = [[-9092791511155847987, 'timmy', 'red'], [-8522787127447073495, 'barry', 'green'], [-6480567542315338377, 'guido', 'blue']] 对象未提供的进一步行为。 OrderedDicts是可逆的,提供顺序敏感的方法,主要提供顺序敏感的相等测试(dict==)。 !=目前没有提供任何这些行为/方法。

<子> [2]:新的字典实现通过更紧凑的设计表现出更好的记忆明智;这是主要的好处。速度方面,差异并不是那么激烈,新的字典可能会引入轻微的回归(key-lookups, for example),而在其他地方(迭代和调整大小会浮现在脑海中)应该是性能提升当下。

<子> 总的来说,字典的性能,特别是在现实生活中,由于引入的紧凑性而得到改善

答案 1 :(得分:61)

下面是回答原来的第一个问题:

  

我应该在Python 3.6中使用dict还是OrderedDict

我认为文档中的这句话实际上足以回答你的问题

  

此新实现的顺序保留方面被视为实现细节,不应依赖

dict并未明确指定为有序集合,因此如果您希望保持一致并且不依赖于新实现的副作用,则应坚持使用OrderedDict

让您的代码面向未来:)

关于correct way的辩论。

编辑: Python 3.7会将此作为一项功能 here

答案 2 :(得分:17)

更新: Guido van Rossum announced on the mailing list从Python 3.7 dict开始,所有Python实现都必须保留插入顺序。

答案 3 :(得分:1)

要在2020年完全回答这个问题,让我引用official Python docs的几句话:

在3.7版中更改:保证字典顺序为插入顺序。此行为是3.6版CPython的实现细节。

在3.7版中进行了更改:保证字典顺序为插入顺序。

在3.8版中进行了更改:词典现在是可逆的。

词典和字典视图是可逆的。

关于OrderedDict与Dict的statement

订购字典与常规字典一样,但是具有一些与订购操作有关的额外功能。现在,内置的dict类获得了记住插入顺序的能力(Python 3.7中已保证了这种新行为),因此它们变得不那么重要了。

答案 4 :(得分:0)

我想添加到上面的讨论中,但没有评论的声誉。

Python 3.8尚未发布,但它甚至在字典中包含reversed()函数(与OrderedDict消除了另一个区别。

  

Dict和dictview现在可以使用reversed()以相反的插入顺序进行迭代。 (由RémiLapeyre在bpo-33462中贡献。)   See what's new in python 3.8

我没有看到关于等价运算符或OrderedDict的其他功能的任何提及,因此它们仍然不完全相同。