当前位置: 首页 > 知识库问答 >
问题:

为什么字典排序是非确定性的?

苏波涛
2023-03-14

我最近从Python2.7切换到Python3.3,在Python2中,字典键的顺序似乎是任意的,但是一致的,而在Python3中,使用例如vars()获得的字典键的顺序似乎是不确定的。

如果我跑:

class Test(object): pass
parameters = vars(Test)
print(list(parameters.keys()))

在Python 2.7和Python 3.3中,然后:

>

['__dict__', '__module__', '__weakref__', '__doc__']

使用Python 3.3,我可以获得任意随机顺序–例如:

['__weakref__', '__module__', '__qualname__', '__doc__', '__dict__']
['__doc__', '__dict__', '__qualname__', '__module__', '__weakref__']
['__dict__', '__module__', '__qualname__', '__weakref__', '__doc__']
['__weakref__', '__doc__', '__qualname__', '__dict__', '__module__']

这种非决定论从何而来?为什么像这样的东西

list({str(i): i for i in range(10)}.keys())

…在跑步之间保持一致,始终给予

['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']

...?

共有2个答案

魏彦
2023-03-14

请注意,Python 3.7仍然具有非确定性集。DICT保留插入顺序,但集合不保留。集合可以显示相同的随机行为。

python3-c“打印({str(i)表示范围(9)中的i})”

仍然会在每次运行中给出不同的结果。

孟选
2023-03-14

更新:在Python3.6中,dict有一个新的实现,它保留了插入顺序。在Python 3.7中,这种保序行为得到了保证:

dict对象的插入顺序保留性质已被声明为Python语言规范的正式部分。

这是2012年安全修复的结果,在Python 3.3中默认启用(向下滚动到“安全改进”)。

从公告中:

哈希随机化导致dicts和set的迭代顺序不可预测,并且在Python运行中各不相同。Python从来没有保证过一个判决或集合中键的迭代顺序,建议应用程序永远不要依赖它。从历史上看,判决迭代顺序在发布期间不会经常改变,并且在Python的连续执行之间总是保持一致。因此,一些现有的应用可能依赖于判决或集合排序。正因为如此,并且许多不接受不受信任输入的Python应用程序不容易受到这种攻击,在这里提到的所有稳定的Python版本中,HASH RANDOMIZATION默认禁用。

如上所述,最后一个大写位在Python 3.3中不再正确。

另请参见:对象__散列(文档(“注意”侧栏)。

如果绝对必要,您可以通过将PYTHONHASHSEED环境变量设置为0来禁用受此行为影响的Python版本中的哈希随机化。

你的反例:

list({str(i): i for i in range(10)}.keys())

…实际上在Python 3.3中并不总是给出相同的结果,尽管由于处理哈希冲突的方式,不同排序的数量是有限的:

$ for x in {0..999}
> do
>   python3.3 -c "print(list({str(i): i for i in range(10)}.keys()))"
> done | sort | uniq -c
     61 ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
     73 ['1', '0', '3', '2', '5', '4', '7', '6', '9', '8']
     62 ['2', '3', '0', '1', '6', '7', '4', '5', '8', '9']
     59 ['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']
     58 ['4', '5', '6', '7', '0', '1', '2', '3', '8', '9']
     55 ['5', '4', '7', '6', '1', '0', '3', '2', '9', '8']
     62 ['6', '7', '4', '5', '2', '3', '0', '1', '8', '9']
     63 ['7', '6', '5', '4', '3', '2', '1', '0', '9', '8']
     60 ['8', '9', '0', '1', '2', '3', '4', '5', '6', '7']
     66 ['8', '9', '2', '3', '0', '1', '6', '7', '4', '5']
     65 ['8', '9', '4', '5', '6', '7', '0', '1', '2', '3']
     53 ['8', '9', '6', '7', '4', '5', '2', '3', '0', '1']
     62 ['9', '8', '1', '0', '3', '2', '5', '4', '7', '6']
     52 ['9', '8', '3', '2', '1', '0', '7', '6', '5', '4']
     73 ['9', '8', '5', '4', '7', '6', '1', '0', '3', '2']
     76 ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0']

正如这个答案的开头所指出的,在Python 3.6中不再是这样了:

$ for x in {0..999}
> do
>   python3.6 -c "print(list({str(i): i for i in range(10)}.keys()))"
> done | sort | uniq -c
   1000 ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
 类似资料:
  • 问题内容: 我最近从Python 2.7切换到Python 3.3,似乎在Python 2中字典键的顺序是任意的,但是是一致的,而在Python 3中,使用例如获得的字典键的顺序似乎是不确定的。 如果我运行: 在Python 2.7和Python 3.3中,则: Python 2.7一直给我 使用Python 3.3,我可以获得任何随机顺序–例如: [‘doc’, ‘dict’, ‘qualnam

  • 问题内容: 如果我想将的结果(即对象)与需要字典或类映射对象的方法一起使用(请参见collections.Mapping),那么正确的方法是什么? “进入”对象并使用其属性是否合适? 我认为答案是否定的:闻起来像执行公约,而不是一个接口,方式或或似乎是。 问题答案: 您可以使用 vars() 访问名称空间的字典: 您可以根据需要直接修改字典: 是的,可以访问__dict__属性。这是定义明确,经过

  • 问题内容: 所以我有一个字典,打印时看起来像这样: 我想以我定义的自定义方式对它们进行排序。假设我希望按键排序的方式是。 有人知道如何以预定义/自定义的方式整理字典吗?我正在做的是从数据库中获取此词典,它可以带有20多个键,所有键都有特定的顺序。总是设置顺序,但有时某些键/值不会出现在字典中。所以这也可能发生: (按键排序)为。 因此,在此示例中不存在10,但是我需要的排序仍然相同,仅缺少10。

  • 有一个问题,为什么他们要求在字典中使用不可变对象作为键。 当我最近使用字典(显然不是为了哈希表)将Xml节点对象作为键放置时,这个问题实际上进入了我的脑海。然后,我在使用过程中多次更新节点。 那么,“使用不可变键”到底意味着什么呢?

  • 问题 你想创建一个字典,并且在迭代或序列化这个字典的时候能够控制元素的顺序。 解决方案 为了能控制一个字典中元素的顺序,你可以使用 collections 模块中的 OrderedDict 类。 在迭代操作的时候它会保持元素被插入时的顺序,示例如下: from collections import OrderedDict d = OrderedDict() d['foo'] = 1 d['bar

  • 或者也许有更好的框架专门为这类场景设计? 谢了。

  • 问题内容: 为什么字典键必须是不可变的?我正在寻找一个简单明了的原因,为什么Python字典中的键具有该限制。 问题答案: 在我的计算机上,有一个包含大量英语单词的文件: 让我们创建一个字典来存储所有这些单词的长度: 并且,为了踢球,我们将改组原始单词列表: 嗯,滚刀。无论如何…现在我们已经有点混乱了,我们变得有点偏执了(可能出于与渴望滚刀相同的原因),并且我们想检查字典中的所有单词是否都正确。我

  • 我们对特使的电路断路进行的实验表明,结果并不确定。我们尝试使用如下设置故意跳闸电路,证明了这一点: 该服务是一个简单的Web服务器,它返回具有2秒时间延迟的(该时间延迟确保服务器在异步请求之间保持忙碌)。我们特使Sidecar配置的快照显示,我们启用断路(超文本传输协议/1.1),最多1个连接和1个挂起的请求: 接下来,我们通过向服务发送单个请求来测试这一点,它会像预期的那样可靠地响应。 但是,如