繁体   English   中英

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

[英]Why is dictionary ordering non-deterministic?

我最近从 Python 2.7 切换到 Python 3.3,似乎虽然在 Python 2 中字典键的顺序是任意但一致的,但在 Python 3 中,使用例如vars()获得的字典键的顺序似乎是不确定的。

如果我运行:

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

在 Python 2.7 和 Python 3.3 中,则:

  • Python 2.7 始终给我

    ['__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']

……?


更新:在 Python 3.6 中, dict有一个保留插入顺序的新实现 从 Python 3.7 开始,这种顺序保留行为得到保证

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


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

从公告:

散列随机化导致字典和集合的迭代顺序不可预测并且在 Python 运行中不同。 Python 从未保证 dict 或 set 中键的迭代顺序,建议应用程序永远不要依赖它。 从历史上看,dict 迭代顺序在不同版本之间并没有经常改变,并且在 Python 的连续执行之间始终保持一致。 因此,一些现有的应用程序可能依赖于 dict 或 set 排序。 由于这一点以及许多不接受不受信任输入的 Python 应用程序不易受到这种攻击的事实,在此处提到的所有稳定的 Python 版本中,默认情况下禁用哈希随机化。

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

另请参阅: object.__hash__()文档(“注意”侧边栏)。

如果绝对必要,您可以通过将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 3.7 仍然具有非确定性集合。 dicts 保留插入顺序,但集合不保留。 集合可以表现出相同的随机行为。

python3 -c "print({str(i) for i in range(9)})"

从一次运行到下一次运行仍然给出不同的结果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM