字典在python 3.6中排序(至少在cpython实现下),与以前的版本不同。这似乎是一个实质性的变化,但它只是文档中的一小段。它被描述为一个cpython实现细节,而不是一个语言特性,但也意味着这可能成为未来的标准。
在保持元素顺序的同时,新的字典实现如何比旧的实现执行得更好?
以下是文档中的文本:
dict() now uses a"compact" representation pioneered by PyPy. The memory usage of the new dict() is between 20% and 25% smaller compared to Python 3.5. PEP 468 (Preserving the order of **kwargs in a function.) is implemented by this. The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon (this may change in the future, but it is desired to have this new dict implementation in the language for a few releases before changing the language spec to mandate order-preserving semantics for all current and future Python implementations; this also helps preserve backwards-compatibility with older versions of the language where random iteration order is still in effect, e.g. Python 3.5). (Contributed by INADA Naoki in issue 27350. Idea originally suggested by Raymond Hettinger.)
号
更新日期:2017年12月:保证为python 3.7提供dict的保留插入命令
- 在python dev邮件列表上看到这个线程:mail.python.org/pipermail/python dev/2016九月/146327.h‌&8203;tml,如果您还没有看到它;它基本上是关于这些主题的讨论。
- 注意,很久以前(2003年),Perl实现者决定将哈希表(相当于Python字典)不仅显式地无序化,而且出于安全原因随机化(perldoc.perl.org/perlsec.html算法复杂性攻击‌&误8203;s)。所以我绝对不会指望这个"特性",因为如果其他人的经验可以作为一个指导,它可能被认为是在某一点上被颠倒了…
- Raymon Hettinger提供的信息包括新dict的原始代码配方。有趣的是,他说:"在新dict出现时,人们的情绪与订购的dict相反,因此这个[原始]配方有意用列表中的最后一个条目填充删除的值。"
- 如果Kwargs现在应该是订购的(这是个好主意),而Kwargs是dict,而不是ordereddict,那么我想有人会认为dict键在将来的python版本中将保持订购状态,尽管文档中另有说明。
- @德米特里辛索夫,不,不要这样假设。这是在定义**kwargs的保序特性的PEP的编写过程中提出的一个问题,因此使用的措辞是外交的:函数签名中的**kwargs现在被保证是一个保序插入映射。他们使用术语映射是为了不强制任何其他实现对dict进行排序(并在内部使用OrderedDict),并且作为一种表示这不应该依赖于dict没有排序的事实的方法。
- Raymond Hettinger的一个很好的视频解释
- @Wazoox,hashmap的顺序和复杂性没有改变。这种改变通过减少浪费空间使hashmap变小,节省的空间(通常是?)比辅助阵列需要的更多。更快、更小、有序-您可以选择所有3个。
- 在python 3.7中,有没有办法让OrderedDict自动转换为普通dict,或者必须通过测试运行的python版本来手动切换?
- @马提诺也许值得单独问一个问题,但我不知道。我想切换的性能优势是温和的。另外,即使在python 3.7 stackoverflow.com/questions/50872498/&hellip中,您可能仍然需要一个OrderedDict;
- 克里斯:链接答案中的好点。我认为有大量的OrderedDict用例不使用这些"高级"功能,这就是我问的原因,但很容易测试使用的是什么版本的python,并在它们可以互换使用时选择您想要的版本。
Are dictionaries ordered in Python 3.6+?
号
它们是按插入顺序排列的[1]。从python 3.6开始,对于python的cpython实现,字典记住插入项的顺序。在Python3.6中,这被视为一个实现细节;如果您希望在其他Python实现中保证插入顺序(以及其他顺序行为[1]),则需要使用OrderedDict。
从Python3.7开始,这不再是一个实现细节,而是一个语言特性。来自gvr的python dev消息:
Make it so."Dict keeps insertion order" is the ruling. Thanks!
号
这就意味着你可以依靠它。如果希望成为python 3.7的一致实现,python的其他实现还必须提供插入顺序字典。
How does the Python 3.6 dictionary implementation perform better[2] than the older one while preserving element order?
号
基本上,通过保留两个数组。
第一个数组,dk_entries按照插入的顺序保存字典的条目(PyDictKeyEntry类型)。保留顺序是通过将新项始终插入末尾(插入顺序)的仅附加数组来实现的。
第二个是dk_indices持有dk_entries数组的索引(即表示dk_entries中相应条目位置的值)。此数组用作哈希表。当一个键被散列时,它会导致存储在dk_indices中的一个索引,并通过索引dk_entries获取相应的条目。由于只保留索引,因此该数组的类型取决于字典的总体大小(从int8_t型(1字节)到int32_t/int64_t型(4/8字节)在32/64位构建上的范围)。
在以前的实现中,必须分配一个类型为PyDictKeyEntry和大小为dk_size的稀疏数组;不幸的是,由于性能原因,该数组不允许超过2/3 * dk_size的满空间,因此也导致了大量的空空间。(空的空间仍然有PyDictKeyEntry大小!).
现在情况并非如此,因为只存储所需的条目(已插入的条目),并保留一个类型为intX_t(X的稀疏数组(取决于dict大小),2/3 * dk_size已满。空位由PyDictKeyEntry型变为intX_t型。
因此,显然,创建类型为PyDictKeyEntry的稀疏数组比存储int的稀疏数组需要更多的内存。
您可以在python dev上看到关于这个特性的完整对话,如果您感兴趣,这是一个很好的阅读。
在RaymondHettinger最初提出的建议中,可以看到所使用的数据结构的可视化,它抓住了这个想法的要点。
For example, the dictionary:
1
| d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'} |
is currently stored as:
1 2 3 4 5 6 7 8
| entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']] |
Instead, the data should be organized as follows:
1 2 3 4
| indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']] |
号
正如您现在看到的,在最初的建议中,很多空间基本上是空的,以减少碰撞并使查找更快。使用新方法,您可以通过移动索引中真正需要的稀疏度来减少所需的内存。
【1】:我说的是"插入有序"而不是"有序",因为"有序"的存在表明了dict对象不提供的进一步行为。顺序图是可逆的,提供顺序敏感的方法,主要提供顺序敏感的相等测试(==,!=)。dict目前不提供任何这些行为/方法。
[2]:新的字典实现通过更紧凑的设计来实现更好的内存方面的性能;这是这里的主要好处。速度方面,差异并不是那么大,有些地方新的dict可能会引入轻微的回归(例如,关键查找),而在其他地方(迭代和调整大小),性能应该得到提升。
总的来说,字典的性能,特别是在现实生活中,由于引入了紧凑性而得到提高。
- 那么,当一个项目被移除时会发生什么?entries列表是否调整了大小?还是留有空白?或者它经常被压缩?
- @njzk2当一个项目被删除时,相应的索引被值为-2的DKIX_DUMMY替换,entry数组中的条目被值为NULL替换,当执行插入时,新的值被附加到条目数组中,还不能识别,但很确定当索引超过e时会填充。执行docx1〔5〕阈值调整。如果存在多个DUMMY条目,这可能导致收缩而不是增长。
- 您注意到新的dict实现在速度方面有什么不同吗?
- @克里斯·兰兹没有,我看到的唯一一个真正的回归是维克托在追踪器上的一条信息。除了那个微基准,我没有看到任何其他的问题/消息表明实际工作负载中存在严重的速度差异。在有些地方,新的dict可能会引入轻微的回归(例如,关键查找),而在其他地方(迭代和调整大小时会想到),性能会得到提升。
- 调整大小部分的更正:删除项目时字典不调整大小,重新插入时字典会重新计算。因此,如果一个dict是用d = {i:i for i in range(100)}创建的,而您.pop所有不插入的项,那么大小不会改变。当您再次添加时,d[1] = 1将计算出适当的大小,并调整dict的大小。
- @jimfasarakishilliard indices列表中的值是什么?如何将0、1、2转换为实际对象?只是为了清楚起见,还是这是列表中的实际值?我以为它能保持钥匙的价值。
- 有没有想过将来OrderedDict会发生什么,我想它会保持向后兼容性?目前OrderedDict支持reversed()迭代和OrderedDict.move_to_end()方法,但可能也会添加到普通dict中?
- @克里斯·兰兹,我很确定它会留下来。问题是,我之所以改变我的回答,删除关于‘dict’正在订购,dict’的笼统陈述,并不是从OrderedDict’的意义上订购的。值得注意的问题是平等。dicts具有顺序不敏感的==,OrderedDicts具有顺序敏感的==。倾销OrderedDicts,将dicts改为现在的具有订单敏感性的比较,可能导致旧代码中的大量破坏。我猜,唯一可能改变OrderedDict的是它的实现。
- 这里可以找到相关的S.O讨论。
- @Jimfasarakishilliard感谢您的详细回答。我把它翻译成韩文,然后分发给facebook集团python korea。blog.sinwoobang.me/post/176050610602/pythondictorder.许多韩国的Python从你的岗位上得到帮助。再次感谢。
下面是第一个原始问题的答案:
Should I use dict or OrderedDict in Python 3.6?
号
我认为文档中的这句话实际上足以回答你的问题
The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon
号
dict并非明确表示要进行有序收集,因此,如果您希望保持一致,而不依赖于新实现的副作用,则应坚持使用OrderedDict。
使您的代码成为未来的证据:)
这里有一个争论。
编辑:python 3.7将保留这一特性,请参见
- 看起来,如果他们不是说这是一个真正的特性,而是仅仅是一个实现细节,那么他们甚至不应该把它放进文档中。
- 我不确定您的编辑警告;因为保证只适用于python 3.7,所以我假设python 3.6的建议是不变的,即dict是在cpython中订购的,但不依赖于它。
更新:guido van rossum在邮件列表中宣布,从python 3.7dict开始,所有python实现中都必须保留插入顺序。
- 既然关键订购是官方标准,那么订购的ICT的目的是什么?或者,现在是多余的吗?
- 我想ordereddict不会是多余的,因为它有move_to_end方法,并且它的相等性是顺序敏感的:docs.python.org/3/library/…。见吉姆·法萨拉基斯·希利亚德回答的注释。
- @JonnyWaffles看到Jim的答案,这个问题和stackoverflow.com/questions/50872498/…
- 如果希望代码在2.7和3.6/3.7+上运行相同的代码,则需要使用ordereddict
- 很可能很快就会有一个"无序的口述",对于那些出于安全原因喜欢麻烦口述的人来说;p