TypeError: unhashable type错误本质是对象__hash__返回None或未实现,且自定义了__eq__;内置不可变类型有哈希,可变容器刻意禁用;自定义类可哈希须同时满足:__hash__返回int、__eq__与__hash__基于相同不可变属性计算。

对象为什么报 TypeError: unhashable type
Python 报这个错,本质是对象的 __hash__ 方法返回了 None,或压根没实现,同时 __eq__ 又被自定义过。内置不可变类型(如 str、int、tuple)默认有稳定哈希值;而可变容器(如 list、dict、set)直接禁用哈希——不是忘了写,是刻意设计。
常见踩坑场景:
- 把自定义类实例塞进
set或当dict的 key,但没实现__hash__ - 实现了
__eq__却没配对实现__hash__,导致 Python 自动把__hash__设为None - 在
__hash__里引用了可变属性(比如某个list字段),后续修改该属性后哈希值突变,破坏字典/集合内部结构
让自定义类可哈希:必须同时满足三个条件
缺一不可。只写 __hash__ 不够,只写 __eq__ 更糟。
-
__hash__方法必须返回一个整数(int),不能返回float、str或其他类型 -
__eq__和__hash__必须基于同一组属性计算——如果两个实例__eq__返回True,它们的__hash__值必须完全相等 - 参与哈希计算的所有属性,在对象生命周期内必须保持不变(即类应设计为“逻辑不可变”);否则哈希表查找会失效
示例:
立即学习“Python免费学习笔记(深入)”;
class Point:
def __init__(self, x, y):
self._x = x # 用下划线暗示只读
self._y = y
<pre class="brush:php;toolbar:false;">@property
def x(self):
return self._x
@property
def y(self):
return self._y
def __eq__(self, other):
if not isinstance(other, Point):
return False
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y)) # 元组可哈希,且内容不可变
本文档主要讲述的是SCA介绍及应用实例;SCA(Service Component Architecture)是针对SOA提出的一套服务体系构建框架协议,内部既融合了IOC的思想,同时又把面向对象的复用由代码复用上升到了业务模块组件复用,同时将服务接口,实现,部署,调用完全分离,通过配置的形式灵活的组装,绑定。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
__hash__ 返回 None 的两种典型情况
这不是 bug,是 Python 的显式约束机制。
- 当你重写了
__eq__但没定义__hash__,Python 会自动把__hash__设为None——哪怕父类原本有哈希能力(比如继承自object) - 显式在
__hash__中写return None,效果一样:实例变成不可哈希对象
验证方式很简单:
print(Point(1, 2).__hash__()) # 输出一个整数
print(list().__hash__()) # 报 AttributeError
print({}.__hash__()) # 同样报 AttributeError
注意:AttributeError 和 TypeError: unhashable type 是不同错误层级——前者是没这个方法,后者是方法存在但返回了 None。
性能与兼容性:哈希值稳定性比“快”更重要
很多人想优化 __hash__ 计算速度,但真正关键的是稳定性:只要对象逻辑上“相等”,哈希值就必须恒定,且不能依赖外部状态(如时间、全局计数器、随机数)。
- 避免在
__hash__中调用len()、sum()等可能因副作用改变结果的操作 - 不要用
id()当哈希值——它随对象内存地址变化,且不同运行周期不一致,无法用于持久化或跨进程场景 - 如果对象字段含嵌套可变结构(如
list字段),要么拒绝哈希,要么转成tuple再哈希(前提是确认该list真的不会再变)
最常被忽略的一点:哈希逻辑一旦上线,就不能随意改动。哪怕只是调整字段顺序或加个默认值,都可能导致缓存失效、集合查找不到原有 key——因为哈希值变了。









