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/33810024/

相关文章:

python - multiprocessing.pool.map 是否删除映射对象?

python - 将列表转换为 NumPy 数组

python - 如何在Python中访问模板?

c# - 具有固定键的字典上的多线程

qt - 检测 QLineEdit 或 QPushButton 上的输入

python - Django 管理员 : JSONField default empty dict wont save in admin

python - 在python中有条件地匹配两个数据库

python - 从 txt 文件中读取单词 - Python

python - 您可以(在 python 脚本内部)将另一个脚本排入队列以在第一个脚本完全退出后运行吗?

python - 如何更好的理解对于 "longest valid parentheses"问题的解决方案?