什么?python dict字典有序了?!
结论 太长不看系列
有人问我为啥我先放结论呢,因为下面内容太多了,不想看又想找结论的同学们帮你们节约时间。
从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]
]
# 类似这个样子
通过这个列表,不难看出每两个元素之前存在一些空隙,这些空隙当然是给后续进来的值准备的啦,我们来看看是怎么实现插入的。
- 通过hash函数对key进行hash,得到hash value;
- mask做与(and)操作,这里设定mask为
字典最小长度 - 1
,得到一个数字(index),这就是要插入的entries哈希表中的位置; - 若index下标位置已经被占用,则会判断entries的key是否与要插入的key是否相等;
- 如果key相等就表示key已存在,则更新value值;
- 如果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],["--", "--", "--"],
]
具体的实现过程又有怎样的变化呢?
- 通过hash函数对key进行hash,得到hash value;
- mask做与(and)操作,这里设定mask为
字典最小长度 - 1
,得到一个数字(index),这就是要插入的indices索引表中的位置; - 得到index后,对应到indices的位置,但是此位置不是存的hash value,而是存序号,表示该值在entries中的位置;
- 若无冲突,则插入entries中的相应元素;
- 冲突处理与之前一致。
下面从代码实现上我们再来看看
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字典有序了?!相关推荐
- Python dict字典(详解)
文章目录 Python dict字典 一,Python创建字典 1) 使用 { } 创建字典 2) 通过 fromkeys() 方法创建字典 3) 通过 dict() 映射函数创建字典 二,Pytho ...
- Python dict字典基本操作
由于字典属于可变序列,所以可以任意操作字典中的键值对(key-value).Python 中,常见的字典操作有以下几种: 向现有字典中添加新的键值对. 修改现有字典中的键值对. 从现有字典中删除指定的 ...
- gensim将python dict字典形式的词向量导入到word2vec模型(同时适合gensim4.0+版本)
先上代码,解释在后: 代码 注意:本代码的gensim 版本适合4.0.0及以上,低于此版本的代码请参考:https://stackoverflow.com/questions/45981305/co ...
- python dict 字典 清空
方法1.利用 {} 清空字典值,会创建一份内存拷贝 >>> x = {} >>> y = x >>> x['key'] = 'value' > ...
- 谷歌浏览器请求头格式化输出成 python dict字典
因为经常使用head头去请求网站,每次复制太麻烦,写了个util方法,记录一下. 只需要把谷歌请求头复制下来把heads替换掉就能用 heads = ''' :authority: www.zhihu ...
- python脚本例子_python dict 字典 以及 赋值 引用的一些实例(详解)
最近在做一个很大的数据库方面的东东,要用到根据数值来查找,于是想到了python中的字典,平时没用过dict这个东东 用的最多的还是 list 和 tuple (网上查 用法一大堆) 看了一下创建字典 ...
- python英语字典程序修改_详解如何修改python中字典的键和值
我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...
- python字典修改键所对应值_详解如何修改python中字典的键和值
我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...
- python实现字典遍历稳定有序使用collection包OrderedDict
python实现字典遍历稳定有序使用collection包OrderedDict **注意虽然python3.6之后,dict本身也会保留插入的顺序,但是并不是严格保证的: Python3.6中的字典 ...
最新文章
- fox pro删除单条数据_删库之后不要着急跑路,教你神不知鬼不觉找回数据
- JDBC驱动程序的四种方式
- 小工匠聊架构文章一览【不间断持续更新】
- 大促场景系统稳定性保障实践经验分享
- 2004-11-3 + 扩展Forms验证
- 软件项目管理四个核心价值观
- css颜色渐变 移动,CSS颜色渐变
- processing一个作品_当你触摸到一束光| 交互灯光装置课程学员作品回顾
- 安全清理大部分的C盘内存(一般10GB以上)
- 为什么登录赛尔号显示服务器未开启,赛尔号之勇者无敌无法打开怎么办 赛尔号之勇者无敌登录不了解决方案...
- ttk progress bar的显示
- 基于Lua的游戏服务端框架简介
- 大学计算机应用教程实验步骤,大学计算机基础实验教程--详细介绍
- ctfshow—2023愚人杯wp
- 【水果识别】柑橘质量检测及分级系统【含GUI Matlab源码 738期】
- python 赌场掷骰子游戏
- linux tar exclude 多个目录,tar 过滤多个文件目录 打包
- 2020-8-18js练习
- 人月神话 四十周年_14个神话般的节日礼物
- 【踩坑笔记】从零开始在Linux和Windows部署安装 ***