为什么字典排序是不确定的? [英] Why is dictionary ordering non-deterministic?

查看:45
本文介绍了为什么字典排序是不确定的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近从 Python 2.7 切换到 Python 3.3,似乎虽然在 Python 2 中字典键的顺序是任意但一致的,但在 Python 3 中,字典键的顺序是通过例如vars() 似乎是不确定的.

I recently switched from Python 2.7 to Python 3.3, and it seems that while in Python 2 the ordering of dictionary keys was arbitrary but consistent, in Python 3 the ordering of the keys of a dictionary obtained with e.g. vars() appears non-deterministic.

如果我跑:

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

在 Python 2.7 和 Python 3.3 中,则:

in both Python 2.7 and Python 3.3, then:

  • Python 2.7 始终为我提供

  • Python 2.7 consistently gives me

['__dict__', '__module__', '__weakref__', '__doc__']

  • 使用 Python 3.3,我可以获得任何随机顺序——例如:

  • With Python 3.3, I can get any random order – for example:

    ['__weakref__', '__module__', '__qualname__', '__doc__', '__dict__']
    ['__doc__', '__dict__', '__qualname__', '__module__', '__weakref__']
    ['__dict__', '__module__', '__qualname__', '__weakref__', '__doc__']
    ['__weakref__', '__doc__', '__qualname__', '__dict__', '__module__']
    

  • 这种不确定性从何而来?为什么是这样的

    Where does this non-determinism come from? And why is something like

    list({str(i): i for i in range(10)}.keys())
    

    ...在运行之间保持一致,总是给予

    … consistent between runs, always giving

    ['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']
    

    ……?

    推荐答案


    更新:在 Python 3.6 中,dict 有一个 新实现 保留插入顺序.从 Python 3.7 开始,这种保留顺序的行为是保证:


    Update: In Python 3.6, dict has a new implementation which preserves insertion order. From Python 3.7, this order-preserving behaviour is guaranteed:

    dict 对象的插入顺序保留性质已声明成为 Python 语言规范的官方部分.

    the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.

    <小时>

    这是安全修复的结果 从 2012 年开始,在 Python 3.3 中默认启用(向下滚动到安全改进").


    This is the result of a security fix from 2012, which was enabled by default in Python 3.3 (scroll down to "Security improvements").

    来自公告:

    哈希随机化导致dicts和sets的迭代顺序为不可预测且因 Python 运行而异.Python从来没有保证字典或集合中键的迭代顺序,建议应用程序永远不要依靠它.从历史上看,dict 迭代顺序并没有经常改变发布并且在连续执行之间始终保持一致Python.因此,一些现有的应用程序可能依赖于 dict 或 set 排序.由于这一点以及许多不接受的 Python 应用程序的事实在所有稳定的 Python 版本中,不受信任的输入不容易受到这种攻击此处提到,默认情况下禁用哈希随机化.

    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 中不再正确.

    As noted above, the last, capitalized bit is no longer true in Python 3.3.

    另见: object.__hash__() 文档(注意"侧边栏).

    See also: object.__hash__() documentation ("Note" sidebar).

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

    If absolutely necessary, you can disable hash randomization in versions of Python affected by this behaviour by setting the PYTHONHASHSEED environment variable to 0.

    你的反例:

    list({str(i): i for i in range(10)}.keys())
    

    ...确实不是实际上在 Python 3.3 中总是给出相同的结果,尽管不同排序的数量有限由于处理散列冲突的方式:

    … does not in fact always give the same result in Python 3.3, although the number of different orderings is limited due to the way hash collisions are handled:

    $ 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 不再是这种情况:

    As noted at the beginning of this answer, that's no longer the case in 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']
    

    这篇关于为什么字典排序是不确定的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆