版本说明

  • Python 3.5 与之前的版本中,迭代字典(dict)时所看到的顺序好像是任意的,不一定与当初把这些键值对添加到字典时的顺序相同。
    • 也就是说,字典不保证迭代顺序与插入顺序一致。
  • Python 3.6 开始,字典会保留这些键值对在添加时所用的顺序。
  • 而且 Python 3.7 版的语言规范正式确立了这条规则。
  • 于是,在新版的 Python 里,总是能够按照当初创建字典时的那套顺序来遍历这些键值对。

    老版本 Python 3.5

    1. sudo add-apt-repository ppa:deadsnakes/ppa
    2. sudo apt-get update
    3. sudo apt install python3.5
    4. # 切换
    5. python3.5

    小例子 - 看出顺序不一致

    1. baby_names = {
    2. 'dog': 'puppy',
    3. 'cat': 'kitten',
    4. }
    5. print(baby_names)
  • 每次看到的顺序不固定

image.png

因为字典类型以前是用哈希表算法来实现的(这个算法通过内置的 hash 函数与一个随机的种子数来运行,而该种子数会在每次启动 Python 解释器时确定)。 所以,这样的机制导致这些键值对在字典中的存放顺序不一定会与添加时的顺序相同,而且每次运行程序的时候,存放顺序可能都不一样。

  • 然而版本 3.6 以后就不会出现这种状况。

    存在问题

  • 函数的关键字参数,使函数调用操作变得很难调试。 ```python

    Python 3.5

    def my_func(**kwargs): for key, value in kwargs.items():

    1. print('%s = %s' % (key, value))

my_func(goose=’gosling’, kangaroo=’joey’) ``` image.png

新版本 3.6 以后

  • 现在的 Python 语言规范已经要求,字典必须保留添加键值对时所依照的顺序。

    其实,内置的 collections 模块早就提供了这种能够保留插入顺序的字典,叫作 OrderedDict

    • 它的行为跟(Python 3.7 以来的)标准 dict 类型很像,但性能上有 很大区别。

    如果要频繁插入或弹出键值对(例如要实现 least-recently-used 缓存),

    • 那 么 OrderedDict 可能比标准的 Python dict 类型更合适。

依旧不要过分依赖顺序一致

  • 在具体的实现过程中,如果加上使用迭代器,可能会导致与预计的结果不一致的问题。

    总结

  • 从 Python 3.7 版开始,我们就可以确信迭代标准的字典时所看到的顺序跟这些键值对插入字典时的顺序一致。

  • 在 Python 代码中,我们很容易就能定义跟标准的字典很像但本身并不是 dict 实例的对象。对于这种类型的对象,不能假设迭代时看到的顺序必定与插入时的顺序相同。
  • 如果不想把这种跟标准字典很相似的类型也当成标准字典来处理,那么可以考虑这样三种办法。
    • 第一,不要依赖插入时的顺序编写代码;
    • 第二,在程序运行时明确判断它是不是标准的字典;
    • 第三,给代码添加类型注解并做静态分析。