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

2024-04-28 04:04:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近从Python2.7切换到Python3.3,似乎在Python2中,字典键的顺序是任意的,但是是一致的,在Python3中,用^{}等方法获得的字典键的顺序似乎是不确定的。

如果我跑:

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

在Python2.7和Python3.3中,然后:

  • Python 2.7一直给我

    ['__dict__', '__module__', '__weakref__', '__doc__']
    
  • 使用Python3.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']


Tags: 方法testdoc字典顺序keysdictpython3
2条回答

更新:在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.


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

从公告中:

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.

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

另请参见:^{} documentation(“注意”侧边栏)。

如果绝对必要,可以通过将^{}环境变量设置为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']

如本答案开头所述,Python3.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']

请注意,Python3.7仍然有不确定的集合。dict保留插入顺序,但set不保留。集合可以表现出相同的随机行为。

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

每次跑步都会有不同的结果。

相关问题 更多 >