You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 09:19:28