Python字典底层实现原理详解


Posted in Python onDecember 18, 2019

在Python中,字典是通过散列表或说哈希表实现的。字典也被称为关联数组,还称为哈希数组等。也就是说,字典也是一个数组,但数组的索引是键经过哈希函数处理后得到的散列值。哈希函数的目的是使键均匀地分布在数组中,并且可以在内存中以O(1)的时间复杂度进行寻址,从而实现快速查找和修改。哈希表中哈希函数的设计困难在于将数据均匀分布在哈希表中,从而尽量减少哈希碰撞和冲突。由于不同的键可能具有相同的哈希值,即可能出现冲突,高级的哈希函数能够使冲突数目最小化。Python中并不包含这样高级的哈希函数,几个重要(用于处理字符串和整数)的哈希函数是常见的几个类型。

通常情况下建立哈希表的具体过程如下:

数据添加:把key通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。

数据查询:再次使用哈希函数将key转换为对应的数组下标,并定位到数组的位置获取value。

哈希函数就是一个映射,因此哈希函数的设定很灵活,只要使得任何关键字由此所得的哈希函数值都落在表长允许的范围之内即可。本质上看哈希函数不可能做成一个一对一的映射关系,其本质是一个多对一的映射,这也就引出了下面一个概念?哈希冲突或者说哈希碰撞。哈希碰撞是不可避免的,但是一个好的哈希函数的设计需要尽量避免哈希碰撞。

Python2中使用使用开放地址法解决冲突。

CPython使用伪随机探测(pseudo-random probing)的散列表(hash table)作为字典的底层数据结构。由于这个实现细节,只有可哈希的对象才能作为字典的键。字典的三个基本操作(添加元素,获取元素和删除元素)的平均事件复杂度为O(1)。

Python中所有不可变的内置类型都是可哈希的。

可变类型(如列表,字典和集合)就是不可哈希的,因此不能作为字典的键。

常见的哈希碰撞解决方法:

1 开放寻址法(open addressing)

开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。

开放地址的意思是除了哈希函数得出的地址可用,当出现冲突的时候其他的地址也一样可用,常见的开放地址思想的方法有线性探测再散列,二次探测再散列等,这些方法都是在第一选择被占用的情况下的解决方法。

2 再哈希法

这个方法是按顺序规定多个哈希函数,每次查询的时候按顺序调用哈希函数,调用到第一个为空的时候返回不存在,调用到此键的时候返回其值。

3 链地址法

将所有关键字哈希值相同的记录都存在同一线性链表中,这样不需要占用其他的哈希地址,相同的哈希值在一条链表上,按顺序遍历就可以找到。

4 公共溢出区

其基本思想是:所有关键字和基本表中关键字为相同哈希值的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

5 装填因子α

一般情况下,处理冲突方法相同的哈希表,其平均查找长度依赖于哈希表的装填因子。哈希表的装填因子定义为表中填入的记录数和哈希表长度的比值,也就是标志着哈希表的装满程度。直观看来,α越小,发生冲突的可能性就越小,反之越大。一般0.75比较合适,涉及数学推导。

在python中一个key-value是一个entry,

entry有三种状态。

Unused: me_key == me_value == NULL

Unused是entry的初始状态,key和value都为NULL。插入元素时,Unused状态转换成Active状态。这是me_key为NULL的唯一情况。

Active: me_key != NULL and me_key != dummy 且 me_value != NULL

插入元素后,entry就成了Active状态,这是me_value唯一不为NULL的情况,删除元素时Active状态刻转换成Dummy状态。

Dummy: me_key == dummy 且 me_value == NULL

此处的dummy对象实际上一个PyStringObject对象,仅作为指示标志。Dummy状态的元素可以在插入元素的时候将它变成Active状态,但它不可能再变成Unused状态。

为什么entry有Dummy状态呢?这是因为采用开放寻址法中,遇到哈希冲突时会找到下一个合适的位置,例如某元素经过哈希计算应该插入到A处,但是此时A处有元素的,通过探测函数计算得到下一个位置B,仍然有元素,直到找到位置C为止,此时ABC构成了探测链,查找元素时如果hash值相同,那么也是顺着这条探测链不断往后找,当删除探测链中的某个元素时,比如B,如果直接把B从哈希表中移除,即变成Unused状态,那么C就不可能再找到了,因为AC之间出现了断裂的现象,正是如此才出现了第三种状态---Dummy,Dummy是一种类似的伪删除方式,保证探测链的连续性。

提示

一般情况下普通的顺序表数组存储结构也可以认为是简单的哈希表,虽然没有采用哈希函数(取余),但同样可以在O(1)时间内进行查找和修改。但是这种方法存在两个问题:扩展性不强;浪费空间。

set集合和dict一样也是基于散列表的,只是他的表元只包含键的引用,而没有对值的引用,其他的和dict基本上是一致的,所以在此就不再多说了。并且dict要求键必须是能被哈希的不可变对象,因此普通的set无法作为dict的键,必须选择被“冻结”的不可变集合类:frozenset。顾名思义,一旦初始化,集合内数据不可修改。

以上这篇Python字典底层实现原理详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
Apr 09 Python
python提取字典key列表的方法
Jul 11 Python
Python实现全排列的打印
Aug 18 Python
python 对key为时间的dict排序方法
Oct 17 Python
使用Pandas对数据进行筛选和排序的实现
Jul 29 Python
Python编程中类与类的关系详解
Aug 08 Python
使用python获取邮箱邮件的设置方法
Sep 20 Python
布隆过滤器的概述及Python实现方法
Dec 08 Python
Python类和实例的属性机制原理详解
Mar 21 Python
opencv 图像加法与图像融合的实现代码
Jul 08 Python
用Python实现童年贪吃蛇小游戏功能的实例代码
Dec 07 Python
Python+SeaTable实现计算两个日期间的工作日天数
Jul 07 Python
Python利用PyExecJS库执行JS函数的案例分析
Dec 18 #Python
简单介绍django提供的加密算法
Dec 18 #Python
详解从Django Allauth中进行登录改造小结
Dec 18 #Python
解决pycharm最左侧Tool Buttons显示不全的问题
Dec 17 #Python
python 字段拆分详解
Dec 17 #Python
从pandas一个单元格的字符串中提取字符串方式
Dec 17 #Python
基于pandas中expand的作用详解
Dec 17 #Python
You might like
PHP+Mysql树型结构(无限分类)数据库设计的2种方式实例
2014/07/15 PHP
Laravel框架中扩展函数、扩展自定义类的方法
2014/09/04 PHP
jquery打开直接跳到网页最下面、最低端实现代码
2013/04/22 Javascript
sencha touch 模仿tabpanel导航栏TabBar的实例代码
2013/10/24 Javascript
jQuery获取和设置表单元素的方法
2014/02/14 Javascript
jquery的attr方法禁用表单元素禁用输入内容
2014/06/23 Javascript
用js通过url传参把数据从一个页面传到另一个页面
2014/09/01 Javascript
ECMAScript5中的对象存取器属性:getter和setter介绍
2014/12/08 Javascript
Node.js开源应用框架HapiJS介绍
2015/01/14 Javascript
javascript中indexOf技术详解
2015/05/07 Javascript
AngularJS中如何使用$http对MongoLab数据表进行增删改查
2016/01/23 Javascript
checkbox 选中一个另一个checkbox也会选中的实现代码
2016/07/09 Javascript
Angularjs中的$apply及优化使用详解
2018/07/02 Javascript
webpack4 入门最简单的例子介绍
2018/09/05 Javascript
浅谈对于react-thunk中间件的简单理解
2019/05/01 Javascript
vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题
2020/04/14 Javascript
[01:09:23]KG vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
python通过ElementTree操作XML获取结点读取属性美化XML
2013/12/02 Python
python根据距离和时长计算配速示例
2014/02/16 Python
Python中Django框架下的staticfiles使用简介
2015/05/30 Python
Python cookbook(数据结构与算法)从任意长度的可迭代对象中分解元素操作示例
2018/02/13 Python
把JSON数据格式转换为Python的类对象方法详解(两种方法)
2019/06/04 Python
弄懂这56个Python使用技巧(轻松掌握Python高效开发)
2019/09/18 Python
将python安装信息加入注册表的示例
2019/11/20 Python
Python中bisect的使用方法
2019/12/31 Python
Petmate品牌官方网站:宠物用品
2018/11/25 全球购物
高级销售员求职信
2013/10/25 职场文书
物流管理专业职业生涯规划书
2014/01/06 职场文书
音乐兴趣小组活动总结
2014/07/07 职场文书
社区关爱留守儿童活动方案
2014/08/22 职场文书
学生违纪检讨书200字
2014/10/21 职场文书
成本会计实训报告
2014/11/05 职场文书
高中教师个人总结
2015/02/10 职场文书
2015年护理工作总结范文
2015/04/03 职场文书
研究生论文答辩开场白
2015/05/27 职场文书
回复函范文
2015/07/14 职场文书