说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在 Python 中,__hash__()
是一个特殊方法(也称为魔术方法或者魔法方法),用于定义对象的哈希值。哈希值是一种唯一地表示对象的整数值,通常用于在哈希表等数据结构中快速查找对象。
当你需要将自定义对象用作字典的键或者集合的元素时,需要实现 __hash__()
方法。这个方法应该返回一个整数,该整数表示对象的哈希值。同时,为了保证哈希值的正确性,还需要实现 __eq__()
方法来定义对象的相等性。
object.__hash__(self)
通过内置函数 hash() 调用以对哈希集的成员进行操作,属于哈希集的类型包括 set、frozenset 以及 dict。__hash__()
应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。
例如:
def __hash__(self):
return hash((self.name, self.nick, self.color))
备注 hash() 会从一个对象自定义的 __hash__()
方法返回值中截断为 Py_ssize_t 的大小。通常对 64 位构建为 8 字节,对 32 位构建为 4 字节。如果一个对象的 __hash__()
必须在不同位大小的构建上进行互操作,请确保检查全部所支持构建的宽度。做到这一点的简单方法是使用 python -c "import sys; print(sys.hash_info.width)"
。
如果一个类没有定义 __eq__()
方法,那么它也不应该定义 __hash__()
操作;如果它定义了 __eq__()
但没有定义 __hash__()
,则其实例将不可被用作可哈希多项集的条目。 如果一个类定义了可变对象并实现了 __eq__()
方法,则它不应该实现 __hash__()
,因为 hashable 多项集的实现要求键的哈希值是不可变的(如果对象的哈希值发生改变,它将位于错误的哈希桶中)。
用户定义的类默认带有 __eq__()
和 __hash__()
方法;使用它们与任何对象(自己除外)比较必定不相等,并且 x.__hash__()
会返回一个恰当的值以确保 x == y
同时意味着 x is y
且 hash(x) == hash(y)
。
一个类如果重载了 __eq__()
且没有定义 __hash__()
则会将其 __hash__()
隐式地设为 None。当一个类的 __hash__()
方法为 None 时,该类的实例将在一个程序尝试获取其哈希值时正确地引发 TypeError,并会在检测 isinstance(obj, collections.abc.Hashable)
时被正确地识别为不可哈希对象。
如果一个重载了 __eq__()
的类需要保留来自父类的 __hash__()
实现,则必须通过设置 __hash__ = <ParentClass>.__hash__
来显式地告知解释器。
如果一个没有重载 __eq__()
的类需要去掉哈希支持,则应该在类定义中包含 __hash__ = None
。一个自定义了 __hash__()
以显式地引发 TypeError 的类会被 isinstance(obj, collections.abc.Hashable)
调用错误地识别为可哈希对象。
以下是一个简单的示例,说明了如何实现 __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 == other.x and self.y == other.y
# 创建两个 Point 对象
p1 = Point(1, 2)
p2 = Point(1, 2)
# 将对象用作字典的键
my_dict = {p1: 'value'}
# 获取 p2 对应的值
print(my_dict[p2]) # 输出:value
在这个示例中,我们定义了一个 Point 类,并实现了 __hash__()
方法和 __eq__()
方法。__hash__()
方法返回了一个元组的哈希值,而 __eq__()
方法用于比较两个 Point 对象的相等性。当我们将两个相同坐标的 Point 对象用作字典的键时,它们会被视为相等的,因此 my_dict[p2]
能够获取到相应的值。
在默认情况下,str 和 bytes 对象的 __hash__()
值会使用一个不可预知的随机值“加盐”。 虽然它们在一个单独 Python 进程中会保持不变,但它们的值在重复运行的 Python 间是不可预测的。
这是为了防止通过精心选择输入来利用字典插入操作在最坏情况下的执行效率即 O(n2) 复杂度制度的拒绝服务攻击。 请参阅 http://ocert.org/advisories/ocert-2011-003.html 了解详情。
改变哈希值会影响集合的迭代次序。Python 也从不保证这个次序不会被改变(通常它在 32 位和 64 位构建上是不一致的)。
另见 PYTHONHASHSEED.
在 3.3 版本发生变更: 默认启用哈希随机化。
https://docs.python.org/zh-cn/3/reference/datamodel.html#object.__hash__
更新时间:March 3, 2024, 8:51 p.m. 标签:python 特殊方法 哈希值