python dict类型底层实现原理

数据结构 HashMap

dict 其实叫做字典是属于直译了
我个人比较喜欢叫他hashmap
因为在不同的编程语言中,数据结构都是大同小异的,迁移到其他编程语言的数据结构,这个结构就叫hashmap

写一段python的伪代码

dict[key] = value;

在python中 字典的keyvalue叫做键值对,

hashmap[hash] = value;

在数据结构hashmap中是以hash值作为键的

其实dict的底层也是给变量存了一个hash值,默认是这个变量的id(也就是地址)

__hash__和__eq__的区别

对于一个自定义的Position类
我们可以重载==运算符,令两个x和y都相等的Position类,在执行equal的时候返回True。

def __eq__(self, other):
	return self.x == other.x and \
		self.y == other.y;
def __hash__(self, other): #如果只重载__eq__的话,baseObject类会认为__hash__不需要继承,导致没有__hash__函数
	return id(self);

但是在hashmap这个结构里,如果想把两个重载了equal的Position类作为相同键值,
要符合hashmap的要求,hashmap数据结构里必须要保证两个equal相等的值,它们的hash也要一样。
hashmap需要比较的第一步是__hash__,而真正存入hashmap键值的是数据结构的地址。

所以hashmap会先去比较两个数据结构的hash,如果两个数据结构的hash是不一样的,那么就认为这两个数据结构是不可能相等的。

想让他们的hash值相同,那么我们最好借用系统给的hash函数,给其中hashable的数据做一个hash,作为我们自定义数据结构的hash。

我们把x和y值变成一个tuple,对他做hash,把这个hash值作为我们自定义object的hash。

def __eq__(self, other):
	return self.x == other.x and \
		self.y == other.y;
def __hash__(self, other): #如果只重载__eq__的话,baseObject类会认为__hash__不需要继承,导致没有__hash__函数
	return hash((self.x, self.y));

如果把__eq__重载去掉,那么又会变成两个key值,因为根据hashtable的原理,两个不同的数据结构有可能拥有相同的hash,这个叫hash collision(哈希冲突),在比较完hash之后一定还要比较这两个数据是不是一样的。

哈希冲突:举个例子

如果对于字符串的hash函数是加和运算
an 的ascii码 97 + 110
和cl 的ascii码 99 + 108是相等的
就会产生hash冲突,就像MD5一样,hash值只能代表原始数据的部分特征,对于高精度的运算,还是需要进行精确的比较。

不重载__eq__的话,自定义数据结构会去比较他们的id是不是一样的。

C层面的比较

hashmap在最开始,还进行了一段C层面的比较,优先比较新的key和原来hash相同位置的key是不是相等的。
这个层面比较的实际上是这两个数据结构实例在C中对应的指针。
所以两个地址相同的key,无论__hash__、__eq__是否相等,最后一定代表的是同一个键。

key的顺序

在python3.6之前是插入无序,在python3.6后有序。

https://blog.csdn.net/weixin_43955170/article/details/119276566

没有任何对象的hash值是-1

在python底层,-1作为C层哈希的保留值,python层无法获得
所以,-1的hash值是magic number,它并不是-1,所以很容易产生哈希冲突

比如先后插入key,-1、-2、-3
最终dict中的结果会变成-2,-1,-3

你可能感兴趣的:(python底层算法篇,python,算法,人工智能)