我最近从Python 2.7切换到Python 3.3,似乎在Python 2中字典键的顺序是任意的,但是是一致的,而在Python 3中,使用例如获得的字典键的顺序vars()似乎是不确定的。
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
dict对象的插入顺序保留性质已声明是Python语言规范的正式组成部分。
这是自2012年以来的安全修复程序的结果,该安全修复程序在Python 3.3中默认启用(向下滚动至“安全改进”)。
从公告中:
散列随机化导致dicts和sets的迭代顺序不可预测,并且在Python运行之间有所不同。Python从未保证字典或集合中键的迭代顺序,建议应用程序不要依赖它。从历史上看,dict的迭代顺序在各个发行版中并没有经常改变,并且在连续的Python执行之间始终保持一致。因此,某些现有应用程序可能依赖于dict或set排序。因此,许多不接受不受信任的输入的Python应用程序都不容易受到此攻击的影响,因此在此处提到的所有稳定的Python版本中,默认情况下都禁用了HASH随机化。
如上所述,最后一个大写的位在Python 3.3中不再适用。
另请参阅: object.__hash__()文档(“注意”侧栏)。
object.__hash__()
如果绝对必要,可以通过将PYTHONHASHSEED环境变量设置为来禁用受此行为影响的Python版本中的哈希随机化0。
PYTHONHASHSEED
0
您的反例:
......不 不 ,其实总是给予同样的结果在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']