Python 3.6 和 Python 3.5 中字典顺序之间的差异 [重复]

Posted

技术标签:

【中文标题】Python 3.6 和 Python 3.5 中字典顺序之间的差异 [重复]【英文标题】:Differences between Dictionary orders in Python 3.6 and Python 3.5 [duplicate] 【发布时间】:2019-09-30 10:22:23 【问题描述】:

我试图了解python 3.6.7python 3.5.2 中python 字典之间的区别。它们存储键值对顺序的方式似乎不同。

例如,假设有一个名为 di 的字典:

    di = 'a':1,'A':1,'b':2, 'B':2, 'c':3, 'C':3

Python 3.5.2,当我打印di时,输出是:

    'C': 3, 'a': 1, 'A': 1, 'B': 2, 'c': 3, 'b': 2

但是,在Python 3.6.7 中,它是:

    'a': 1, 'A': 1, 'b': 2, 'B': 2, 'c': 3, 'C': 3

两个版本之间有什么变化? 我怎样才能让我的代码订购python 3.6.7 的结果类似于3.5.2 的结果。

附:我知道 Python 字典中实际上没有 no order。这里使用order这个词是为了让读者容易理解我的问题。 谢谢。

【问题讨论】:

从 Python 3.6+ 开始,python 字典按插入顺序存储键值对。 相关:***.com/questions/39980323/… 您认识到字典中没有顺序 - 顺序取决于实现,并且可能会在未来版本中更改。因此,根据这样的顺序,一个错误必然会在某个时间点或另一个点破坏您的代码 @ArkistarvhKltzuonstev dict() 在 py3.6 中排序的事实是一个实现细节。如果需要有序行为,则应使用 OrderedDict。实际上,编辑 3.7 规范了 dict() 的插入顺序性质。不过,如果您想要订单,请使用 OrderedDict。 通常大多数人不需要依赖字典键顺序,除非有特殊要求。如果您需要的是导出字典以生成 JSON 文件,那么 json.dump 模块实际上有一个关键顺序参数。 【参考方案1】:

TLDR:

要复制基于哈希的排序,您必须采用明确排序的dict 并强制执行您自己的排序。

from collections import OrderedDict

def cpy35dict(source_dict):
    """Apply hash-ordering to a dictionary"""
    return OrderedDict(  # ensure stable ordering
        sorted(          # order items by key hash
            source_dict.items(),
            key=lambda item: hash(item[0])
        )
    )

这将 CPython 中使用的排序复制到 Python 3.5。 请注意,Python 语言规范no 保证在 Python 3.7 之前的顺序 - 如果您坚持顺序,您也应该在 Python 3.5 及之前使用它。


dict-like 容器的订单基本分为三种:

无顺序:物品没有特定的顺序。每次访问都可能返回不同的顺序。 特定顺序:物品有明确的顺序。每次访问都返回相同的顺序。 任意顺序:顺序未定义。它可能使用也可能不使用特定顺序。

根据 Python 规范,dict 在 Python 3.6 之前具有任意顺序,在 Python 3.7 之后具有插入顺序

在 3.7 版更改:保证字典顺序为插入顺序。这种行为是 CPython 3.6 的实现细节。 [Mapping Types — dict¶]

但是,任意顺序不排除特定顺序。它基本上意味着“无论实施感觉如何”。不同的实现使用不同的方案来实现dict

PyPy 使用插入顺序,因为Python 2.7/3.2

CPython 3.5- 使用底层散列函数的排序。由于几种类型都有加盐哈希,这意味着每次调用都会得到不同的顺序。

CPython 3.6 使用插入项目的顺序。这是一个明确的实现细节。

此新实现的顺序保留方面被视为实现细节,不应依赖 [What's new in Python 3.6]

换句话说,Python 3.6 及更早版本的代码应该假设dict 的排序。只有 Python 3.7 及更高版本的代码才应该对dict 的排序做出假设。

【讨论】:

注意:按哈希排序实际上不会模仿 3.6 之前的行为。问题是 3.6 之前的顺序是基于哈希模数桶的数量(例如,如果底层存储有 16 个桶,则余数为 0 的哈希值 mod 16 通常会出现在一个余数为1 mod 16,即使 0 哈希是 0xFFFFFFF0 并且 1 哈希是 0x1)。除此之外,在发生冲突的情况下(其中两个哈希对桶计数具有相同的值),第二个项目的桶最终可能会完全不同,因此它甚至不会按哈希的低位排序。【参考方案2】:

字典的顺序“允许”在程序的每次运行中都不同,更不用说版本了。事实上,它在每个版本上都被一致地排序为只有 CPython 知道的实现细节*。您的程序不应依赖此行为。

如何让我的代码排序 python 3.6.7 的结果类似于 3.5.2 的结果。

使用OrderedDict!

* 实际上,as of Python 3.7,插入顺序的保存正式是语言规范的一部分。

【讨论】:

虽然我同意 OrderedDict 在这里可能有用,但您能进一步详细说明吗?为了将 'a': 1, 'A': 1, 'b': 2, 'B': 2, 'c': 3, 'C': 3 变成 'C': 3, 'a': 1, 'A': 1, 'B': 2, 'c': 3, 'b': 2,应该使用 OrderedDict 做什么? 这里不是“可能有用”——它是必需的 :) 如果你想要一个有序的字典,请使用 OrderedDict。这甚至保留了order across comparisons。并回答 OP 的子问题,因此您在 cmets 中的问题:除非@Long 希望在 py3.6 中发生 EXACT py3.5 行为,否则我想使用 OrderedDict 就足够了,因为它将确保一致的行为跨越 py3.5 和 py3.6。

以上是关于Python 3.6 和 Python 3.5 中字典顺序之间的差异 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何让 python3 命令运行 Python 3.6 而不是 3.5?

在 conda 中将 python 从 3.5 更新到 3.6

可以在python 3.6中安装python 3.5包吗?

当print()unicode字符时,python 3.5与3.6的差异?

就地自定义对象使用 __getitem__ python 3.5 与 python 3.6 解包不同的行为

使用 pytest 测试 __init__.py 中可选依赖项的导入:Python 3.5 /3.6 的行为不同