自定义Pydantic模型调用.dict()方法时报错TypeError: unhashable type: 'dict'的问题排查及代码优化建议
自定义Pydantic模型调用.dict()方法时报错TypeError: unhashable type: 'dict'的问题排查及代码优化建议
嗨,DD!我来帮你分析这个问题,顺便给你一些代码优化的建议。
问题原因
你遇到的TypeError: unhashable type: 'dict',根源出在Role模型里的subroles字段用了set[SubRole]类型。当你调用.dict()方法时,Pydantic会尝试把每个SubRole实例序列化成字典,但字典是不可哈希的,而集合(set)要求里面的元素必须是可哈希类型——这就导致序列化过程中,集合里突然出现了不可哈希的字典,直接触发了错误。
解决方案
这里有两种可行的解决思路,你可以根据自己的业务需求选择:
方案1:将set替换为list(最简便)
把subroles的类型改成list[SubRole],因为列表不需要元素可哈希,序列化时不会有任何问题。如果需要保证子角色的唯一性,可以在模型里加一个验证器,确保列表里的子角色名称不重复:
class Role(RoleBaseClass): """Role model.""" subroles: list[SubRole] = pydantic.Field(default_factory=list) @pydantic.field_validator("subroles") def check_unique_subroles(cls, v): seen_names = set() for subrole in v: if subrole.name in seen_names: raise ValueError(f"Duplicate subrole name: {subrole.name}") seen_names.add(subrole.name) return v
方案2:保留set但自定义序列化逻辑
如果你一定要用set来存储子角色,可以通过自定义序列化方法,把集合转成列表后再序列化。比如重写序列化逻辑(Pydantic v2推荐用model_dump替代dict):
from pydantic import model_serializer class Role(RoleBaseClass): """Role model.""" subroles: set[SubRole] = pydantic.Field(default_factory=set) @model_serializer def serialize_model(self): base_dict = self.model_dump() base_dict["subroles"] = list(base_dict["subroles"]) return base_dict
代码优化建议
除了解决这个错误,还有几个小细节可以优化你的代码:
- 简化哈希和相等判断:你自己实现的
__hash__和__eq__其实可以用Pydantic内置的配置替代。比如在基类里设置model_config={"frozen": True},这样Pydantic会自动生成基于所有字段的哈希和相等逻辑,省去手动编写的麻烦。 - 类型注解简化:Python 3.11+支持直接用
Self而不需要从typing导入,可以把typing.Self改成Self,代码更简洁。 - 移除冗余配置:比如
# pylint: disable=no-member在Pydantic v2里已经不需要了,因为v2的BaseModel不会触发这个pylint错误。 - 使用default_factory:对于集合或列表类型,推荐用
default_factory=set或default_factory=list代替default=set(),避免在多个实例之间共享同一个集合对象,这是Pydantic的最佳实践。
优化后的代码示例:
# pylint: disable=too-few-public-methods from typing import Self import pydantic class RoleBaseClass(pydantic.BaseModel): """Base class for role-related models.""" name: str = pydantic.Field(regex=r"^\w+$") model_config = {"frozen": True} # 自动生成__hash__和__eq__ class SubRole(RoleBaseClass): """SubRole model representing a sub-role under a main role.""" class Role(RoleBaseClass): """Role model containing a set of unique SubRoles.""" subroles: set[SubRole] = pydantic.Field(default_factory=set) @model_serializer def serialize_model(self): base_dict = self.model_dump() base_dict["subroles"] = list(base_dict["subroles"]) return base_dict def __str__(self) -> str: return f"{self.__class__.__name__}({super().__str__()})"
这样修改后,既解决了序列化的报错问题,代码也更符合Pydantic的最佳实践啦!
备注:内容来源于stack exchange,提问作者DDStackoverflow
相关产品推荐
相关产品推荐

