Python __hash__ 方法
最后修改于 2025 年 4 月 8 日
这篇综合指南探讨了 Python 的 __hash__ 方法,该特殊方法使对象可哈希。我们将介绍基本用法、不可变性要求、字典键、自定义哈希和实际示例。
基本定义
__hash__ 方法返回对象的整数哈希值。此哈希值用于字典键和集合中,以进行快速查找操作。
主要特征:对于相等的对象必须返回相同的哈希值,应该很快,并且应该均匀地分配值。具有 __hash__ 的对象称为可哈希对象,可以作为字典键。
基本 __hash__ 实现
这是一个简单的实现,展示了 __hash__ 如何与不可变对象一起工作。哈希基于对象的属性。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
p1 = Point(1, 2)
p2 = Point(1, 2)
print(hash(p1) == hash(p2)) # True
此示例显示了一个可哈希的 Point 类。哈希基于坐标的元组。相等的点产生相同的哈希值。
请注意,我们还实现了 __eq__。这是保持哈希不变性的必要条件:相等的对象必须具有相等的哈希值。
使对象可哈希
要使对象可哈希,它们必须是不可变的。此示例演示如何创建一个不可变的可哈希类。
class ImmutablePerson:
__slots__ = ('_name', '_age')
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
def __hash__(self):
return hash((self._name, self._age))
def __eq__(self, other):
return (self._name, self._age) == (other._name, other._age)
person = ImmutablePerson("Alice", 30)
d = {person: "value"}
print(d[person]) # "value"
这个不可变的 person 类使用属性和 __slots__ 来防止创建后属性的修改。哈希基于姓名和年龄。
该类可以用作字典键,因为它正确地实现了 __hash__ 和 __eq__ 方法。
自定义哈希逻辑
有时您需要自定义哈希逻辑。此示例演示如何实现不区分大小写的字符串哈希。
class CaseInsensitiveString:
def __init__(self, s):
self._s = s.lower()
def __hash__(self):
return hash(self._s)
def __eq__(self, other):
return self._s == other._s
def __repr__(self):
return f"CaseInsensitiveString('{self._s}')"
s1 = CaseInsensitiveString("Hello")
s2 = CaseInsensitiveString("HELLO")
print(hash(s1) == hash(s2)) # True
d = {s1: "value"}
print(d[s2]) # "value"
此类在哈希之前将字符串规范化为小写,从而使哈希不区分大小写。相等的字符串(不区分大小写)产生相同的哈希值。
__eq__ 方法还比较规范化的字符串以保持哈希不变性。
可哈希容器
此示例演示了如何通过哈希其不可变内容来使自定义容器可哈希。
class HashableList:
def __init__(self, items):
self._items = tuple(items)
def __hash__(self):
return hash(self._items)
def __eq__(self, other):
return self._items == other._items
def __getitem__(self, index):
return self._items[index]
def __len__(self):
return len(self._items)
def __repr__(self):
return f"HashableList({list(self._items)})"
hl1 = HashableList([1, 2, 3])
hl2 = HashableList([1, 2, 3])
d = {hl1: "value"}
print(d[hl2]) # "value"
HashableList 在内部将项目存储在一个元组中,以确保不可变性。哈希基于此元组,使容器可哈希。
当您需要使用列表(或其他可变容器)作为字典键,同时保持不可变性时,此模式非常有用。
何时不实现 __hash__
此示例显示了一个可变类,由于其状态可以更改,因此不应实现 __hash__。
class MutablePoint:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, dx, dy):
self.x += dx
self.y += dy
# No __hash__ implementation
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
p = MutablePoint(1, 2)
# hash(p) would raise TypeError
这个可变 point 类没有实现 __hash__,因为它的坐标可以改变。Python 默认会使其不可哈希。
如果我们尝试在此处实现 __hash__,当点移动时,它会违反哈希不变性,从而可能导致字典查找问题。
最佳实践
- 保持哈希不变性: 相等的对象必须具有相等的哈希值
- 使用不可变属性: 可哈希对象应该是不可变的
- 组合组件的哈希值: 使用元组进行多属性哈希
- 不要哈希可变状态: 避免哈希可以更改的对象
- 实现 __eq__: 在实现 __hash__ 时始终实现 __eq__
资料来源
作者
列出所有 Python 教程。