如何让 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; ?>
】
相关推荐
- Win10如何备份注册表_Win10注册表备份步骤
- Win10如何卸载Skype_Win10卸载Sky
- Win10任务栏天气和资讯怎么关闭 Win10禁用
- Linux如何使用grep搜索文件内容_Linux
- MySQL 中使用 IF 和 CASE 实现查询字
- Mac怎么设置鼠标滚动速度_Mac鼠标设置详细参数
- PHP主流架构如何做单元测试_工具与流程【详解】
- php增删改查报错1054怎么办_字段名错误排查修
- Windows的便笺功能如何使用?(桌面备忘技巧)
- Win11怎么更改管理员名字 Win11修改账户名
- Go语言中slice追加操作的底层共享机制解析
- 如何处理“XML格式不正确”错误 常见XML we
- php嵌入式多设备通信怎么实现_php同时管理多个
- Bpmn 2.0的XML文件怎么画流程图
- Python配置文件操作教程_JSONINIYAM
- c++如何使用std::bitset进行位图算法_
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- Win11怎么设置单手模式_Win11触控键盘布局
- Python装饰器设计思路_功能增强机制说明【指导
- Windows如何设置登录时的欢迎屏幕背景?(锁屏
- Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱
- Python字符串处理进阶_切片方法解析【指导】
- php中常量能用::访问吗_类常量与作用域操作符使
- PHP主流架构如何处理会话管理_Session与C
- 如何使用Golang搭建Web开发环境_快速启动H
- Win11怎么关闭系统声音_Win11系统提示音静
- PowerShell怎么创建复杂的XML结构
- C++中的Pimpl idiom是什么,有什么好处
- Mac如何彻底清理浏览器缓存?(Safari与Ch
- php8.4匿名类怎么用_php8.4匿名类创建与
- Win11怎么设置系统还原_Windows11系统
- Win10如何设置双wan路由器 Win10双wa
- Win11怎么关闭用户账户控制UAC_Window
- Win11此电脑不在桌面上_Windows 11桌
- 如何在 ACF 中正确更新嵌套多层 Group 字
- Windows10电脑怎么查看硬盘通电时间_Win
- Win11如何设置计划任务 Win11定时执行程序
- Win11怎样彻底卸载自带应用_Win11彻底卸载
- Python音视频处理高级项目教程_FFmpegP
- Win11怎么开启空间音效_Windows11耳机
- Windows10如何更改桌面图标间距_Win10
- Mac如何备份到iCloud_Mac桌面与文稿文件
- c# F# 的 MailboxProcessor
- 如何在 Go 中正确初始化结构体中的 map 字段
- 如何使用Golang配置安全开发环境_防止敏感信息
- 如何在Golang中使用内置函数_Golangle
- php转mp4怎么设置帧率_调整php生成mp4视
- php怎么下载安装后设置默认字符集_utf8配置步
- php485返回数据不完整怎么办_php485数据
- Mac如何调整Dock栏大小和位置_Mac程序坞个


QQ客服