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

做~自己de王妃 提交于 2020-02-27 12:16:33

与以前的版本不同,字典在Python 3.6中排序(至少在CPython实现下)。 这似乎是一个实质性的更改,但这只是文档中的一小段。 它被描述为CPython实现细节而不是语言功能,但这也意味着将来可能会成为标准。

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

以下是文档中的文字:

dict()现在使用由PyPy开创的“紧凑”表示形式。 与Python 3.5相比,新dict()的内存使用量减少了20%至25%。 PEP 468 (在函数中保留** kwarg的顺序。)由此实现。 此新实现的顺序保留方面被认为是实现细节,因此不应依赖(将来可能会更改,但是希望在更改语言规范之前,先在几个发行版中使用该新dict实现该语言,为所有当前和将来的Python实现强制要求保留顺序的语义;这还有助于保留与仍旧有效的随机迭代顺序的旧版本语言(例如Python 3.5)的向后兼容性。 (由INADA Naoki在Issue 27350中提供 。该想法最初由Raymond Hettinger提出 。)

2017年12月更新:Python 3.7 保证 dict保留插入顺序


#1楼

以下是回答原始的第一个问题:

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

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

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

dict并不是明确地意味着是有序集合,因此,如果要保持一致并且不依赖于新实现的副作用,则应坚持使用OrderedDict

使您的代码成为未来的证明:)

有关于辩论在这里

编辑: Python 3.7将保留此功能, 请参阅


#2楼

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

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

从Python 3.7开始 ,它不再是实现细节,而是成为一种语言功能。 从GvR的py​​thon-dev消息中

做到这一点。 裁定“裁定保留插入顺序”。 谢谢!

这只是意味着您可以依靠它 。 如果其他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'}

当前存储为[keyhash,key,value]:

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]:新的字典实现通过更紧凑的设计而在内存方面表现更好;这是这里的主要好处。在速度方面,差异并不那么明显,在某些地方,新dict可能会引入轻微的回归( 例如,关键查找 ),而在其他地方(想到迭代和调整大小),则应该提高性能。

总体而言,由于引入的紧凑性,字典的性能(尤其是在现实生活中)得以提高。


#3楼

更新:Guido van Rossum 在邮件列表宣布,从 Python 3.7 dict ,所有Python实现中必须保留插入顺序。


#4楼

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

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

现在可以使用reversed()以反向插入顺序迭代Dict和dictviews。 (由RémiLapeyre在bpo-33462中贡献。) 查看python 3.8的新增功能

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!