我最近从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']
......?
答案 0 :(得分:45)
更新:在Python 3.6中,dict
有new implementation,保留了广告订单。从Python 3.7开始,这种保留顺序的行为是guaranteed:
dict个对象has been declared的插入顺序保存性质是Python语言规范的官方部分。
这是2012年security fix的结果,在Python 3.3中为enabled by default(向下滚动到“安全性改进”)。
宣布:
哈希随机化导致dicts和sets的迭代顺序为 Python运行中不可预测且不同。 Python从未保证过 dict或set中键的迭代顺序,建议应用程序永远不要 依靠它。从历史上看,dict迭代顺序并没有经常发生变化 释放并且在连续执行之间始终保持一致 蟒蛇。因此,一些现有的应用程序可能依赖于字典或集合排序。 因为这和许多Python应用程序不接受的事实 在所有稳定的Python版本中,不受信任的输入都不容易受到此攻击 这里提到的,HASH RANDOMIZATION被默认禁用。
如上所述,Python 3.3中的最后一个大写位不再成立。
另请参阅: object.__hash__()
documentation(“注意”侧栏)。
如果绝对必要,可以通过将PYTHONHASHSEED
环境变量设置为0
来禁用受此行为影响的Python版本中的哈希随机化。
您的反例:
list({str(i): i for i in range(10)}.keys())
... 不实际上总是在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']
答案 1 :(得分:2)
请注意,Python 3.7仍然具有不确定性集。字典保留插入顺序,但集合不保留。集可以表现出相同的随机行为。
python3 -c "print({str(i) for i in range(9)})"
从一次运行到下一次运行仍然给出不同的结果。