Python 3.6+ 中的词典是有序的吗?

Are dictionaries ordered in Python 3.6+?

从 Python 3.6 开始,字典按插入顺序排列。它被描述为 CPython 实现细节而不是语言功能。 documentation 状态:

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 月更新:dicts 保留插入顺序为 guaranteed for Python 3.7

下面回答原第一题:

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.

让您的代码面向未来:)

对此有争论here

编辑:Python 3.7 将保留此功能

Are dictionaries ordered in Python 3.6+?

它们是插入顺序[1].

从Python 3.6开始,对于Python的CPython实现,字典记住顺序已插入项目这被认为是 Python 3.6 中的一个实现细节;你需要使用 OrderedDict 如果你想要 保证 的插入顺序跨越 Python 的其他实现(和其他有序行为 [1]).

从 Python 3.7 开始,这是一个有保证的语言特性,而不仅仅是一个实现细节。 From a python-dev message by GvR:

Make it so. "Dict keeps insertion order" is the ruling. Thanks!

这只是意味着您可以信赖它。 Python 的其他实现如果希望成为 Python 3.7.

的一致实现,也必须提供插入顺序字典

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

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

  • 字典的第一个数组,dk_entries, holds the entries (of type PyDictKeyEntry) 按照插入顺序排列。保留顺序是通过这是一个仅附加数组来实现的,其中新项目总是插入到末尾(插入顺序)。

  • 第二个,dk_indices, holds the indices for the dk_entries array (that is, values that indicate the position of the corresponding entry in dk_entries). This array acts as the hash table. When a key is hashed it leads to one of the indices stored in dk_indices and the corresponding entry is fetched by indexing dk_entries. Since only indices are kept, the type of this array depends on the overall size of the dictionary (ranging from type int8_t(1 byte) to int32_t/int64_t4/8字节)在32/64位构建)

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

现在情况并非如此,因为只存储了 必需的 条目(已插入的条目)和 intX_t 类型的稀疏数组(X 取决于 dict 大小) 2/3 * dk_sizes full 被保留。空 space 从类型 PyDictKeyEntry 更改为 intX_t.

因此,很明显,创建类型为 PyDictKeyEntry 的稀疏数组比存储 ints 的稀疏数组需要更多的内存。

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


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

For example, the dictionary:

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

is currently stored as [keyhash, key, value]:

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

Instead, the data should be organized as follows:

indices =  [None, 1, None, None, None, 0, None, 2]
entries =  [[-9092791511155847987, 'timmy', 'red'],
            [-8522787127447073495, 'barry', 'green'],
            [-6480567542315338377, 'guido', 'blue']]

正如您现在可以直观地看到的那样,在最初的提议中,很多 space 基本上是空的,以减少冲突并加快查找速度。使用新方法,您可以通过在索引中真正需要的地方移动稀疏性来减少所需的内存。


[1]:我说 "insertion ordered" 而不是 "ordered",因为随着 OrderedDict 的存在,"ordered" 暗示了 `dict` 对象*不提供*的进一步行为。 OrderedDicts 是可逆的,提供顺序敏感的方法,主要是提供顺序敏感的相等性测试(`==`、`!=`)。 `dict` 目前不提供任何这些 behaviors/methods。
[2]:新的字典实现通过更紧凑的设计表现出更好的**内存明智**;这是这里的主要好处。在速度方面,差异并不是那么大,新字典在某些地方可能会引入轻微的回归(key-lookups, for example),而在其他地方(想到迭代和调整大小)应该会出现性能提升。 总的来说,字典的性能,尤其是在现实生活中,由于引入了紧凑性而得到改善。

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

我想添加到上面的讨论中,但没有资格发表评论。

Python 3.8 在字典中包含 reversed() 功能(删除了与 OrderedDict.

的另一个区别

Dict and dictviews are now iterable in reversed insertion order using reversed(). (Contributed by Rémi Lapeyre in bpo-33462.) See what's new in python 3.8

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

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

Changed in version 3.7: Dictionary order is guaranteed to be insertion order. This behavior was an implementation detail of CPython from 3.6.

Changed in version 3.7: Dictionary order is guaranteed to be insertion order.

Changed in version 3.8: Dictionaries are now reversible.

Dictionaries and dictionary views are reversible.

关于 OrderedDict 与字典的 statement

Ordered dictionaries are just like regular dictionaries but have some extra capabilities relating to ordering operations. They have become less important now that the built-in dict class gained the ability to remember insertion order (this new behavior became guaranteed in Python 3.7).