字典是在Python3.6中排序的(至少在CPython实现下),这与之前的版本不同.这似乎是一个实质性的变化,但这只是documentation篇文章中的一小段.它被描述为CPython实现细节,而不是语言特性,但也意味着这可能在future 成为标准.

在保持元素顺序的同时,新的dictionary实现如何比旧的dictionary实现更好地执行?

以下是文档中的文本:

dict()现在使用"紧凑"表示pioneered by PyPy.与Python 3.5相比,新dict()的内存使用量减少了20%到25%.PEP 468(保留函数中**kwargs的顺序.)这是通过以下方式实现的.这个新实现的顺序保持方面被认为是一个实现细节,不应该依赖它(这在future 可能会发生变化,但在更改语言规范以强制所有当前和future 的Python实现保留顺序语义之前,我们希望在一些版本中使用这种新的dict实现;这也有助于保持与该语言的旧版本的向后兼容性,在旧版本中,随机迭代ionic 顺序仍然有效,例如Python 3.5).(由INADA Naoki于issue 27350年撰稿.创意originally suggested by Raymond Hettinger.)

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

推荐答案

Are dictionaries ordered in Python 3.6+?

它们是insertion ordered[1].从Python 3.6开始,对于Python的CPython实现,字典remember the order of items inserted.This is considered an implementation detail in Python 3.6; 如果希望在其他Python实现中插入顺序为guaranteed(以及其他有序行为[1]),则需要使用OrderedDict.

As of Python 3.7,这不再是一个实现细节,而是一个语言特性.From a python-dev message by GvR:

就这么办吧."DICT保持插入顺序"是裁决.谢谢!

这简单地说就是you can depend on it.如果其他Python实现希望成为符合Python3.7的实现,那么它们也必须提供插入顺序字典.


How does the Python 100 dictionary implementation perform better[2] than the older one while preserving element order?

基本上是keeping two arrays.

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

  • 第二个dk_indices保存dk_entries数组的索引(即,指示dk_entries中相应条目位置的值).这个数组充当哈希表.当密钥被散列时,它导致存储在dk_indices中的索引之一,并且相应的条目由索引dk_entries获取.由于只保留索引,因此该数组的类型取决于字典的总体大小(在32/64位构建中,从类型int8_t(1字节)到int32_t/int64_t(4/8字节)不等)

在之前的实现中,必须分配PyDictKeyEntry类型和dk_size大小的稀疏数组;不幸的是,它也导致了大量的空白空间,因为该数组不允许超过2/3 * dk_sizefor performance reasons.(而still号空位的大小为PyDictKeyEntry!).

现在的情况并非如此,因为只存储了required个条目(已插入的条目),并且保留了类型为intX_t(X取决于dict大小)2/3 * dk_sizes full的稀疏array.空位从PyDictKeyEntry型变为intX_t型.

因此,显然,创建类型为PyDictKeyEntry的稀疏数组比存储int的稀疏数组需要更多内存.

如果感兴趣,您可以查看关于此功能的完整对话on Python-Dev,这是一本不错的读物.


In the original proposal made by Raymond Hettinger,可以看到所使用的数据 struct 的可视化,它抓住了 idea 的要点.

例如,字典:

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]: I say "insertion ordered" and not "ordered" since, with the existence of OrderedDict, "ordered" suggests further behavior that the `dict` object *doesn't provide*. OrderedDicts are reversible, provide order sensitive methods and, mainly, provide an order-sensive equality tests (`==`, `!=`). `dict`s currently don't offer any of those behaviors/methods.
[2]: The new dictionary implementations performs better **memory wise** by being designed more compactly; that's the main benefit here. Speed wise, the difference isn't so drastic, there's places where the new dict might introduce slight regressions (key-lookups, for example) while in others (iteration and resizing come to mind) a performance boost should be present. Overall, the performance of the dictionary, especially in real-life situations, improves due to the compactness introduced.

Python相关问答推荐

如何在Pandas 中存储二进制数?

Python在通过Inbox调用时给出不同的响应

Django序列化器没有验证或保存数据

在两极中实施频率编码

这家einsum运营在做什么?E = NP.einsum(aj,kl-il,A,B)

使用Beautiful Soup获取第二个srcset属性

在Arrow上迭代的快速方法.Julia中包含3000万行和25列的表

jit JAX函数中的迭代器

对Numpy函数进行载体化

如何使用symy打印方程?

为什么符号没有按顺序添加?

如何在类和classy-fastapi -fastapi- followup中使用FastAPI创建路由

转换为浮点,pandas字符串列,混合千和十进制分隔符

改进大型数据集的框架性能

SQLAlchemy bindparam在mssql上失败(但在mysql上工作)

Pandas GroupBy可以分成两个盒子吗?

Python Pandas获取层次路径直到顶层管理

在单次扫描中创建列表

在代码执行后关闭ChromeDriver窗口

如何在Python中使用Iscolc迭代器实现观察者模式?