我最近从Python2.7切换到Python3.3,在Python2中,字典键的顺序是任意的,但是一致的,而在Python3中,使用例如vars()获得的字典键的顺序似乎是不确定的.

如果我 run :

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())

…每次 run 都保持一致,总是给予

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

… ?

推荐答案


Update:在Python 3.6中,dict有一个保留插入顺序的new implementation.在Python 3.7中,这种保序行为是guaranteed:

dict个对象has been declared的插入顺序保留性质将成为Python语言规范的正式部分.


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

从公告中:

散列随机化导致dict和set的迭代顺序为

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

See also: object.__hash__() documentation("注意"侧栏).

如果绝对必要,可以通过将PYTHONHASHSEED环境变量设置为0,在受此行为影响的Python版本中禁用哈希随机化.


你的反例是:

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

…在Python 3.3中,not实际上总是给出相同的结果,尽管不同排序的数量受到哈希冲突处理方式的限制:

$ 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']

Python-3.x相关问答推荐

"安装serial vs安装psyserial header,"""

类型注释:pathlib. Path vs importlib. resources. abc. Traversable

具有多个值的极轴旋转和熔化/取消旋转(反转旋转)操作(Pandas 堆叠/取消堆叠交替/UDF覆盖)

Numpy argmin()以查找最近的元组

将数据帧扩展为矩阵索引

从PYTHON中获取单行和多行的Rguar表达式

使用数据库将文件从Sharepoint下载到文件系统

Python VS Code 自动导入路径包含 src

找到在指定列的另一个分组中存在重复的行.

如何计算Pandas 列中每列唯一项目的出现次数?

python2和python3中的列表生成器

Pandas DataFrame:使用 Pandas 将 NaN 值替换为 3 行以上的平均值

Snakemake 'run' 指令不产生错误信息

使用一周的特定第一天将每日日期转换为每周

没有可重定向到的 URL.提供一个 url 或在模型上定义一个 get_absolute_url 方法

在初始化之前禁用`__setattr__`的干净方法

活动屏幕上的 PyQt4 中心窗口

多个返回函数的列表理解?

Python 3.9.8 使用 Black 并导入 `typed_ast.ast3` 失败

Windows 下 Python 3.x 的 OpenCV