结论 太长不看系列

有人问我为啥我先放结论呢,因为下面内容太多了,不想看又想找结论的同学们帮你们节约时间。

从python3.6开始,dict的插入变为有序,即字典整体变的有序;
而之前的版本,比如python2.7,对于字典的插入是乱序的,即插入a,b,c,返回结果顺序可能是a,c,b。

文章目录

  • 结论 ~~太长不看系列~~
  • 背景
  • 基础知识
    • 什么是字典?
    • 字典的查询、添加、删除的时间复杂度?
    • 字典的key可以重复么?
  • 源码探秘
    • python3.6之前
    • python3.6之后
  • 参考资料

背景

今天在做一个项目的时候,需要用到字典,但是又想数据按照字典key插入的顺序进行排序,根据python2.7的经验,字典是乱序的,于是在查资料的过程中发现了一个宝藏package叫OderedDict,顾名思义就是有序字典,也就是按照插入顺序进行排序的一个字典。然后就用这个开发了呗。

但是一个偶然的机会有人问我能不能不用这个OderedDict,因为还要import这个东西,太麻烦了。我寻思你嫌麻烦还是别写python了吧,这么简单的操作不适合你,但是还是要沉住气,憋住屁,帮他想想办法。然后我就去查python的dict,惊奇的发现python3的字典居然是有序的了?!

这令我十分诧异,于是便有了以下的源码探秘。

基础知识

给一些新手们普及一些基础知识吧,这样在源码探秘过程中会容易理解一下。

什么是字典?

在计算机科学中,关联数组(英语:Associative Array),又称映射(Map)、字典(Dictionary)是一个抽象的数据结构,它包含着类似于(键key,值value)的有序对。字典是一个可变容器,可以存储任意类型的对象。字典的实现和哈希算法也有紧密关系,key和value的映射便是通过哈希(hash)来实现的。

字典的查询、添加、删除的时间复杂度?

平均时间复杂度都是O(1)。注意这里提到的是平均时间复杂度,最坏情况是O(n),后文会提到原因。

字典的key可以重复么?

问这个问题的大概没用过字典吧
通常来讲,不可以重复,如果重复赋值了,后面的值会覆盖前面的值。为啥说通常来讲呢,因为万一以后有什么神人搞了个什么奇怪的概念可以重复了呢(手动狗头)。

源码探秘

python3.6之前

字典的底层实现实际就是一张hash表,简单可以理解为一个列表,列表中的每一个元素又保存了三个元素,分别是哈希值(hash value)、键(key)和值(value)。

entries = [["--", "--", "--"],["--", "--", "--"],[hash_value_1, key1, value1],[hash_value_2, key2, value2]
]
# 类似这个样子

通过这个列表,不难看出每两个元素之前存在一些空隙,这些空隙当然是给后续进来的值准备的啦,我们来看看是怎么实现插入的。

  1. 通过hash函数对key进行hash,得到hash value;
  2. mask做与(and)操作,这里设定mask为字典最小长度 - 1,得到一个数字(index),这就是要插入的entries哈希表中的位置;
  3. 若index下标位置已经被占用,则会判断entries的key是否与要插入的key是否相等;
  4. 如果key相等就表示key已存在,则更新value值;
  5. 如果key不相等,就表示hash conflict,出现哈希冲突,则会继续向下寻找空位置,一直到找到剩余空位为止。因此,若所有位置都被占满,则需要O(n)的时间复杂度来遍历整个列表。

我们通过代码来理解一下这个过程

my_dict = {}
# 给字典添加一个值,key为hello,value为world
my_dict["hello"] = "world"# 假设是一个空列表,hash表初始如下
entries = [["--", "--", "--"],["--", "--", "--"],["--", "--", "--"],["--", "--", "--"]
]hash_value = hash("hello")                 # 假设值为 11111 注:以下计算值均为释例
index = hash_value & ( len(entries) - 1)   # 假设index值计算后等于2# 下面会将值存在entries中
entries = [["--", "--", "--"],["--", "--", "--"],[11111, "hello", "world"], # index=2["--", "--", "--"]
]# 我们继续向字典中添加值
my_dict["benz"] = "car"hash_value = hash("benz")                 # 假设值又为 11111
index = hash_value & ( len(entries) - 1)  # 假设index值计算后同样等于2# 下面会将值存在entries中
entries = [["--", "--", "--"],["--", "--", "--"],[11111, "hello", "world"], # 由于index=2的位置已经被占用,且key不一样,所以判定为hash conflict,向下寻找空位[11111, "benz", "car"]     # 找到空余位置并进行插入
]

通过上面的原理解读和代码流程讲解,我们已经了解了字典的插入的过程。我们可以看到,不同的key通过hash计算的出的index值可能是不一样的,在entries中插入的位置不一样,所以当我们遍历字典的时候,字段的顺序与我们插入的顺序是会出现不相同的。

python3.6之后

以前的字典只有一张简单的hash table,新的字典不仅有一张hash table,还有一张indices索引表加以辅助,于是便可以实现字典的有序。

新的结构:

indices = [index0, index0, index0, None]
entries = [[hash_value_0, key0, value0],[hash_value_1, key1, value1],[hash_value_2, key2, value2],["--", "--", "--"],
]

具体的实现过程又有怎样的变化呢?

  1. 通过hash函数对key进行hash,得到hash value;
  2. mask做与(and)操作,这里设定mask为字典最小长度 - 1,得到一个数字(index),这就是要插入的indices索引表中的位置;
  3. 得到index后,对应到indices的位置,但是此位置不是存的hash value,而是存序号,表示该值在entries中的位置;
  4. 若无冲突,则插入entries中的相应元素;
  5. 冲突处理与之前一致。

下面从代码实现上我们再来看看

my_dict = {}
# 给字典添加一个值,key为hello,value为world
my_dict["hello"] = "world"# 假设是一个空列表,hash表初始如下
indices = [None, None, None, None]
entries = [["--", "--", "--"],["--", "--", "--"],["--", "--", "--"],["--", "--", "--"]
]hash_value = hash('hello')                # 假设值为 11111
index = hash_value & ( len(indices) - 1)  # 假设index值计算后等于2# 会找到indices的index为2的位置,并插入entries的长度
indices = [None, None, 0, None]
# 此时entries会插入这个元素
entries = [["--", "--", "--"],["--", "--", "--"],[11111, "hello", "world"],["--", "--", "--"]
]# 我们继续向字典中添加值
my_dict["benz"] = "car"hash_value = hash("benz")                 # 假设值为 22222
index = hash_value & ( len(indices) - 1)  # 假设index值计算后等于 0# 会找到indices的index为0的位置,并插入entries的长度
indices = [1, None, 0, None]
# 此时entries会修改相应index的值
entries = [[22222, "benz", "car"],["--", "--", "--"],[11111, "hello", "world"],["--", "--", "--"],
]

因此,在有indices表加持的情况下,整个hash table可以变的有序起来,这也就解释了为什么dict可以有序。

参考资料

[Python-Dev] Python 3.6 dict becomes compact and gets a private version; and keywords become ordered. https://mail.python.org/pipermail/python-dev/2016-September/146327.html

什么?python dict字典有序了?!相关推荐

  1. Python dict字典(详解)

    文章目录 Python dict字典 一,Python创建字典 1) 使用 { } 创建字典 2) 通过 fromkeys() 方法创建字典 3) 通过 dict() 映射函数创建字典 二,Pytho ...

  2. Python dict字典基本操作

    由于字典属于可变序列,所以可以任意操作字典中的键值对(key-value).Python 中,常见的字典操作有以下几种: 向现有字典中添加新的键值对. 修改现有字典中的键值对. 从现有字典中删除指定的 ...

  3. gensim将python dict字典形式的词向量导入到word2vec模型(同时适合gensim4.0+版本)

    先上代码,解释在后: 代码 注意:本代码的gensim 版本适合4.0.0及以上,低于此版本的代码请参考:https://stackoverflow.com/questions/45981305/co ...

  4. python dict 字典 清空

    方法1.利用 {} 清空字典值,会创建一份内存拷贝 >>> x = {} >>> y = x >>> x['key'] = 'value' > ...

  5. 谷歌浏览器请求头格式化输出成 python dict字典

    因为经常使用head头去请求网站,每次复制太麻烦,写了个util方法,记录一下. 只需要把谷歌请求头复制下来把heads替换掉就能用 heads = ''' :authority: www.zhihu ...

  6. python脚本例子_python dict 字典 以及 赋值 引用的一些实例(详解)

    最近在做一个很大的数据库方面的东东,要用到根据数值来查找,于是想到了python中的字典,平时没用过dict这个东东 用的最多的还是 list 和 tuple (网上查 用法一大堆) 看了一下创建字典 ...

  7. python英语字典程序修改_详解如何修改python中字典的键和值

    我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...

  8. python字典修改键所对应值_详解如何修改python中字典的键和值

    我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...

  9. python实现字典遍历稳定有序使用collection包OrderedDict

    python实现字典遍历稳定有序使用collection包OrderedDict **注意虽然python3.6之后,dict本身也会保留插入的顺序,但是并不是严格保证的: Python3.6中的字典 ...

最新文章

  1. fox pro删除单条数据_删库之后不要着急跑路,教你神不知鬼不觉找回数据
  2. JDBC驱动程序的四种方式
  3. 小工匠聊架构文章一览【不间断持续更新】
  4. 大促场景系统稳定性保障实践经验分享
  5. 2004-11-3 + 扩展Forms验证
  6. 软件项目管理四个核心价值观
  7. css颜色渐变 移动,CSS颜色渐变
  8. processing一个作品_当你触摸到一束光| 交互灯光装置课程学员作品回顾
  9. 安全清理大部分的C盘内存(一般10GB以上)
  10. 为什么登录赛尔号显示服务器未开启,赛尔号之勇者无敌无法打开怎么办 赛尔号之勇者无敌登录不了解决方案...
  11. ttk progress bar的显示
  12. 基于Lua的游戏服务端框架简介
  13. 大学计算机应用教程实验步骤,大学计算机基础实验教程--详细介绍
  14. ctfshow—2023愚人杯wp
  15. 【水果识别】柑橘质量检测及分级系统【含GUI Matlab源码 738期】
  16. python 赌场掷骰子游戏
  17. linux tar exclude 多个目录,tar 过滤多个文件目录 打包
  18. 2020-8-18js练习
  19. 人月神话 四十周年_14个神话般的节日礼物
  20. 【踩坑笔记】从零开始在Linux和Windows部署安装 ***

热门文章

  1. 爬取TIOBE的编程语言排行榜
  2. 2014暑假学习总结
  3. 计算机智能科学与技术专业大学排名,全国智能科学与技术专业大学排名
  4. XDOJ-314-完全二叉树的子树
  5. git切换分支,暂存修改代码
  6. Python爬虫入门,详细讲解爬虫过程
  7. html5诅咒学园通关,NO.5 诅咒发作
  8. 漫途设备远程运维平台在制造业中的应用!
  9. Java资源大全中文版-Awesome - java
  10. Sencha Touch 与 PhoneGap异同