Python中可哈希对象需满足哈希值不变且等值对象哈希值相同;默认可哈希的有int、float、str、bytes、None、元素全可哈希的tuple、frozenset;list、dict、set因可变不可哈希;自定义类需同时正确定义__hash__和__eq__。
Python中,set 和 dict 的底层依赖对象的哈希值(hash())来实现快速查找和去重。能否放入 set 或作为 dict 的键,取决于该对象是否“可哈希”——核心是:对象必须满足 哈希值在整个生命周期内不变,且 相等的对象必须有相同的哈希值(即遵守 __hash__ 与 __eq__ 的一致性约定)。
不可变内置类型通常可哈希:
int、float、str、bytes、None
tuple)——但仅当其所有元素都可哈希时才可哈希(例如 (1, "a", (2, 3)) ✅;(1, [2]) ❌)set ❌(因可变)因为它们是可变容器:内容改变后,若哈希值不变,就会破坏哈希表结构(比如键查不到、重复插入);若强制改哈希值,又违反“哈希值不可变”原则。所以 Python 直接禁止它们实现 __hash__(返回 NotImplemented),调用 hash() 会报 TypeError。
例如:
hash([1, 2]) # TypeError: unhashable type: 'list'
hash({'a': 1}) # TypeError: unhashable type: 'dict'
hash({1, 2}) # TypeError: unhashable type: 'set'需同时定义 __hash__ 和 __eq__,且保证逻辑一致:
__hash__ 应基于不可变属性计算(推荐用 hash((a, b, c)) 元组哈希)__eq__ 必须用相同属性判断相等性__eq__ 但没定义 __hash__,Python 会自动将 __hash__ 设为 None(即不可哈希)示例:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return isinstance(other, Point) and self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y)) # 基于不可变属性构造元组哈希p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
print(hash(p1) == hash(p2)) # True
s = {p1, p2}
print(len(s)) # 1 —— 正确去重
__hash__ 中使用可变属性(如列表、字典字段),否则哈希值可能随内容变化,导致 set/dict 行为异常__init__ 后禁止修改
关键属性(可用 __slots__ 或属性只读封装)hasattr(obj, '__hash__') and obj.__hash__ is not None 判断是否可哈希dict → frozenset(items()),list → tuple()(确保元素可哈希)