python dict hash_python dict 实现解析

0.dict 浅析

dict 由 key, value pair 组成,python 中是通过 hash 实现的,value 和 key 是一对一的关系,没有效率瓶颈,往往 key list 的查询是更需要关注的效率瓶颈。

1.list 搜索效率

python 用 list 存储 dict 中的 keys,list 是通过连续内存空间来存储的。dict 中经常需要判断 key 是否存在,如果顺序搜索 list 效率很低,极端情况下要检索所有的 key 才能知道是否存在,算法复杂度是 O(n)。这会导致 dict 的效率会随着 key 的增多线性下降,所以 python 设计了 hash 的方法来解决 list 搜索问题。

def simple_search(simple_list, key):

idx = 0

while idx < len(simple_list):

if simple_list[idx] == key:

return True

idx += 1

return False

2.hash 管理 key list

hash 有很多种方式,这里用余数的方式最简单的展示相关过程,即用每个整数值除以 key 的数量取余数,把余数当成 hash value。

def build_not_quite_what_we_want(original_list):

new_list = [None] * len(original_list) # 生成和 key list相同长度的 list 用以存放 key

for number in original_list:

idx = number % len(new_list) # 获得余数

new_list[idx] = number

return new_list

这时会出现 hash collision,因为余数是会重复的,重复的话最后一个 key 会在新的 list 中覆盖之前的 key,这就要求一种处理哈希碰撞的方法。

2.1 linear probing

当出现哈希碰撞时,最简单的处理方式是查找后续可用的 index。新的 hash table 越大碰撞可能性越小,但占用的空间也会越大。平衡这两个参数,一般 hash table 的长度是 key list 长度的两倍。

def build_insert_all(original_list):

new_list = [None] * (2 * len(original_list)) # 生成 2倍

for number in original_list:

idx = number % len(new_list)

while new_list[idx] is not None:

idx = (idx + 1) % len(new_list)

new_list[idx] = number

return new_list

2.2 separate chaining

另一种哈希碰撞的解决方案是在碰撞的 index 的位置存储一个链表,将相同哈希值的内容用链表记录下来。这也是 redis 实现 hash 类型的一种方法。

### 关于 `__hash__` 方法的详细介绍 #### 1. 基本概念 在 Python 中,`__hash__` 是一种魔术方法,用于返回对象的哈希值。该方法的主要作用是支持集合类型(如 `set` 和 `frozenset`)以及字典类型的键存储机制。如果两个对象通过 `__eq__` 判断为相等,则它们的哈希值必须相同[^2]。 #### 2. 使用场景 当自定义类的对象需要作为字典的键或者存入集合时,必须实现 `__hash__` 方法并确保其行为一致性。具体来说: - 如果实现了 `__eq__` 方法来判断对象之间的相等关系,那么也需要同步实现 `__hash__` 方法。 - 对象一旦被用作字典的键或集合的成员,它的哈希值在整个生命周期中都不能发生变化。 #### 3. 实现细节 以下是关于如何正确实现 `__hash__` 的一些注意事项: - **不可变性**:只有不可变的数据结构才能安全地计算哈希值。因此,在设计可变对象时,应考虑将其设置为不可哈希(即让 `__hash__` 返回 `None` 或抛出异常)。 - **一致性**:对于任何两个对象 `a` 和 `b`,如果 `a == b` 成立,则 `hash(a) == hash(b)` 必须成立。 #### 4. 示例代码 下面是一个完整的例子,展示如何在一个自定义类中实现 `__hash__`: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if isinstance(other, Person): return self.name == other.name and self.age == other.age return False def __hash__(self): # 计算基于多个字段组合的稳定哈希值 return hash((self.name, self.age)) # 测试代码 p1 = Person("Alice", 30) p2 = Person("Bob", 20) p3 = Person("Alice", 30) print(hash(p1) == hash(p3)) # 输出 True,因为 p1 和 p3 被认为相等 people_dict = {p1: "some value"} print(people_dict.get(p3)) # 输出 "some value",验证了哈希一致性的效果 ``` #### 5. 特殊情况处理 如果不希望某个类的对象能够参与哈希运算,可以通过显式禁用 `__hash__` 来达到目的。例如: ```python class MutableObject: def __init__(self, data): self.data = data def __eq__(self, other): if isinstance(other, MutableObject): return self.data == other.data return False __hash__ = None # 显式声明此对象不可哈希 try: obj_set = set([MutableObject(1), MutableObject(2)]) except TypeError as e: print(e) # 输出错误提示,表明无法将不可哈希对象加入集合 ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值