如何让 hash 只对 frozen 对象生效且类型安全
技术百科
冰川箭仙
发布时间:2026-01-27
浏览: 次 hash()拒绝未冻结对象是因为可变对象的哈希值不稳定,破坏字典/集合结构;Python通过将__hash__设为None实现约束,@dataclass(frozen=True)、NamedTuple等提供类型安全的哈希支持。
为什么 hash() 会拒绝未冻结对象
hash() 在 Python 中要求对象是“不可变”的,本质是要求 __hash__ 返回稳定值——而可变对象的哈希值可能随内容改变,破坏字典/集合的底层结构。Python 不强制检查是否真的“不可变”,而是约定:若实现了 __eq__ 且没显式定义 __hash__,则自动设为 None(即不可哈希)。所以“只对 frozen 对象生效”不是 hash() 的内置规则,而是你主动控制的结果。
用 @dataclass(frozen=True) 实现类型安全的哈希对象
这是最直接、类型友好的方式。启用 frozen=True 后,dataclass 自动生成带类型注解的 __hash__,且禁止运行时修改字段(触发 FrozenInstanceError)。
实操建议:
- 必须标注所有字段的类型(如
name: str),否则typing工具(如 mypy)无法校验构造参数类型 - 避免在
__post_init__中修改字段——即使 frozen,该方法仍可执行,但后续赋值会报错 - 若含可变默认值(如
list),必须用field(default_factory=list),否则运行时报错
示例:
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
print(hash(p)) # ✅ 正常返回整数
p.x = 3 # ❌ FrozenInstanceError
手动定义 __hash__ 时如何保持类型安全
手动实现适用于需要自定义哈希逻辑(如忽略某些字段),但容易绕过类型检查。关键点在于:确保 __hash__ 只依赖 __eq__ 所依赖的字段,且这些字段本身是不可变类型。
常见错误现象:
- 字段含
list或dict→ 运行时报TypeError: unhashable type - 字段是自定义类但没实现
__hash__→ 哈希失败 - 用了
__slots__却漏写某个参与比较的字段 →hash()和==行为不一致
正确做法:
- 只对
tuple、str、int等原生不可变类型或已哈希的自定义对象取哈希 - 用
typing.Final标注字段(如id: Final[int]),提示类型检查器该字段不应被重写 - 在
__hash__中显式调用hash((self.a, self.b)),而非hash(self.a + self.b)(后者易冲突)
用 NamedTuple 替代时要注意什么
NamedTuple 天然 frozen 且可哈希,也支持类型注解,但它是类工厂,不是普通类——它的字段是只读属性,没有 __dict__,也不支持继承或自定义 __init__。
使用场景:
- 轻量级、纯数据容器(如配置项、坐标点)
- 需和
typing.NamedTuple配合做静态类型检查
容易踩的坑:
- 定义时用
c形式,别用
lass X(NamedTuple):
collections.namedtuple()—— 后者不支持类型注解 - 字段名不能是 Python 关键字(如
class、def),否则生成类失败 - 若字段类型是泛型(如
list[str]),需用typing.List[str](Py3.9+ 可用内置)
类型安全不是靠“加个 frozen 就完事”,而是让类型检查器能追踪到“这个对象一旦创建,哪些属性永远不变”,并阻止任何可能破坏哈希稳定性的赋值路径。真正难的是嵌套结构——比如一个 frozen dataclass 包含另一个可变对象,这时 hash 依然会崩,但错误发生在运行时而非类型检查阶段。
# 的是
# 这是
# 也不
# 它是
# 用了
# 是因为
# python
# 适用于
# 自定义
# 设为
# 工具
# 对象
# int
# class
# 泛型
# 为什么
# 继承
# 只对
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何高效获取循环末次生成的 NumPy 数组最后一
- 如何诊断并终止卡死的 multiprocessin
- Windows7如何安装系统镜像_Windows7
- win11如何清理传递优化文件 Win11为C盘瘦
- Mac如何将HEIC图片格式转为JPG_Mac批量
- c++如何使用std::bind绑定函数参数_c+
- Windows10怎样设置家长控制_Windows
- windows系统找不到无线网络怎么办_windo
- Linux如何安装Golang环境_Linux下G
- 如何自定义Windows终端的默认配置文件?(Po
- XSLT怎么生成动态的HTML属性名和标签名
- Windows怎样关闭Edge新标签页广告_Win
- 如何在 PHP 单元测试中正确模拟带方法的图像处理
- Windows10如何查看保存的WiFi密码_Wi
- 如何在Golang中实现RPC异步返回_Golan
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件
- 如何使用Golang搭建Web开发环境_快速启动H
- Win11如何设置ipv6 Win11开启IPv6
- c# 如何深拷贝和浅拷贝
- Win10怎么更改用户名 Win10修改账户名称操
- 如何使用Golang实现路由分组管理_Golang
- Windows服务启动类型恢复方法_错误修改导致的
- c# Task.Yield 的作用是什么 它和Ta
- c++中的CRTP是什么 c++奇异递归模板模式【
- Python异步网络编程_aiohttp说明【指导
- 如何使用Golang实现Web表单数据绑定_自动映
- 如何在Golang中捕获HTTP服务器错误_Gol
- 如何在 Go 中比较自定义的数组类型(如 [20]
- Win10怎样卸载DockerDesktop_Wi
- Golang如何实现基本的用户注册_Golang用
- Win11任务栏怎么放到顶部_Win11修改任务栏
- 如何使用Golang进行HTTP服务性能测试_测量
- php串口通信波特率怎么选_根据硬件手册设置正确波
- c++的位运算怎么用 与、或、异或、移位操作详解【
- Windows任务计划服务异常原因_任务调度失败的
- c++的STL算法库find怎么用 在容器中查找指
- c++怎么使用std::unique实现去重_c+
- Windows怎样关闭锁屏广告_Windows关闭
- Win11怎么设置指纹解锁 Win11笔记本录入指
- Win11怎样安装钉钉客户端_Win11安装钉钉教
- php怎么操作Redis_Redis扩展连接与基本
- c++如何利用doxygen生成开发文档_c++
- 如何理解Go指针和内存分配关系_Go Pointe
- php485支持哪些操作系统_php485跨系统支
- Python对象生命周期管理_创建销毁解析【教程】
- Win11玩游戏全屏闪退怎么办_Win11全屏优化
- C++如何使用Qt创建第一个GUI窗口?(入门教程
- php修改数据怎么批量改状态_批量更新status
- Windows10系统怎么查看设备管理器_Win1
- Python字符串处理进阶_切片方法解析【指导】


QQ客服