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

标签 python dictionary python-3.x python-3.3 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 有一个 new implementation保留插入顺序。从 Python 3.7 开始,这种保持顺序的行为是 guaranteed :

the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.


这是 security fix 的结果从 2012 年开始,为 enabled by default在 Python 3.3 中(向下滚动到“安全改进”)。

来自公告:

Hash randomization causes the iteration order of dicts and sets to be unpredictable and differ across Python runs. Python has never guaranteed iteration order of keys in a dict or set, and applications are advised to never rely on it. Historically, dict iteration order has not changed very often across releases and has always remained consistent between successive executions of Python. Thus, some existing applications may be relying on dict or set ordering. Because of this and the fact that many Python applications which don't accept untrusted input are not vulnerable to this attack, in all stable Python releases mentioned here, HASH RANDOMIZATION IS DISABLED BY DEFAULT.

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

另见: object.__hash__() documentation (“注意”侧边栏)。

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


你的反例:

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

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

$ 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 - 为什么字典排序是不确定的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14956313/

相关文章:

python - 将一些行转到 DataFrame 中的新列

dictionary - 如何将字典映射到特定类的对象?

c# - 如何重构此 if 语句以添加或更新字典?

python - 用字典中的新值替换键值对中的空白值(python 3.x)

python - Docker pip3未安装软件包

python - 使用神经网络的骑士之旅

python - 在Python3中分析文件中的数据并将其输出到字典中

python - { } 量词如何工作?

python - celery :禁用 worker 之间的心跳

python - 使用带有 for 循环的字典来比较值