代码之家  ›  专栏  ›  技术社区  ›  Anaphory

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

  •  48
  • Anaphory  · 技术社区  · 13 年前

    我最近从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']
    

    ?

    2 回复  |  直到 9 年前
        1
  •  62
  •   Zero Piraeus    7 年前

    更新: 在Python 3.6中, dict 有一个 new implementation 这保持了插入顺序。在Python 3.7中,这种保序行为是 guaranteed :

    插入顺序的保存性质 dict 物体 has been declared 成为Python语言规范的官方部分。


    这是一个 security fix 从2012年开始 enabled by default 在Python 3.3中(向下滚动到“安全性改进”)。

    根据公告:

    哈希随机化导致dicts和set的迭代顺序为 不可预测,并且在Python运行中有所不同。Python从未保证 dict或集合中键的迭代顺序,建议应用程序永远不要 依赖它。从历史上看,dict迭代顺序在 释放,并且在连续执行 蟒蛇因此,一些现有的应用程序可能依赖于dict或集合排序。 正因为如此,而且许多Python应用程序不接受 在所有稳定的Python版本中,不受信任的输入都不易受到此攻击 这里提到,HASH随机化是默认禁用的。

    如上所述,最后一个大写位在Python 3.3中不再为真。

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

    如果绝对必要,可以通过设置 PYTHONHASHSEED 环境变量到 0 .


    你的反例:

    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']
    
        2
  •  14
  •   Pete Cacioppi    7 年前

    请注意,Python 3.7仍然有非确定性集合。dicts保留插入顺序,但set不保留。集合可以表现出相同的随机行为。

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

    从一次运行到下一次运行仍然会给出不同的结果。