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

Pydantic dataclass结合ABC时mypy报classmethod调用参数错误

问题:Pydantic dataclass 结合 ABCMeta 导致 mypy 报关键字参数错误

我有一个基于 Pydantic 的简单 dataclass:

from pydantic.dataclasses import dataclass
from abc import ABCMeta, abstractmethod


@dataclass
class BaseEntity(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def from_dict(cls, other: dict):
        ...

    @abstractmethod
    def dict(self):
        ...


@dataclass
class UserEntity(BaseEntity):
    id: Optional[str]
    name: str
    email: str
    avatar: str

    @classmethod
    def from_dict(cls, other: dict):
        return cls(
            id=other.get("id"),
            name=other.get("name"),
            email=other.get("email"),
            avatar=other.get("avatar"),
        )

运行 mypy 时出现以下错误:

app/entities/user.py:25: error: Unexpected keyword argument "id" for "UserEntity" [call-arg]
app/entities/user.py:25: error: Unexpected keyword argument "name" for "UserEntity" [call-arg]
app/entities/user.py:25: error: Unexpected keyword argument "email" for "UserEntity" [call-arg]
app/entities/user.py:25: error: Unexpected keyword argument "avatar" for "UserEntity" [call-arg]

代码可以正常运行,但为什么会出现这些错误?这是我哪里写错了,还是 mypy 的 bug?

mypy 版本:

$ mypy --version
mypy 1.0.0 (compiled: yes)

原因分析

问题出在同时使用 Pydantic 的 @dataclass 装饰器和 ABCMeta 元类的组合上。Pydantic 的 dataclass 装饰器会动态生成类的构造函数,但 mypy 在处理带有自定义元类的类时,无法正确识别 Pydantic 生成的构造函数参数,因此误判传入的关键字参数不存在。

另外代码中遗漏了 Optional 的导入(from typing import Optional),虽然运行时不会报错,但也会导致额外的类型检查错误,建议补充该导入。

解决方法

方法1:改用 Pydantic BaseModel(推荐)

Pydantic 的 BaseModel 原生支持抽象基类,类型检查兼容性更好,无需额外处理元类:

from pydantic import BaseModel
from typing import Optional
from abc import ABC, abstractmethod


class BaseEntity(ABC, BaseModel):
    @classmethod
    @abstractmethod
    def from_dict(cls, other: dict):
        ...

    @abstractmethod
    def dict(self):
        ...


class UserEntity(BaseEntity):
    id: Optional[str]
    name: str
    email: str
    avatar: str

    @classmethod
    def from_dict(cls, other: dict):
        return cls(**other)

方法2:调整基类继承方式(保留 dataclass)

如果坚持使用 dataclass,可以让基类继承 ABC(ABC 已封装 ABCMeta),代替直接指定 metaclass=ABCMeta,这样 mypy 能正确识别子类的构造参数:

from pydantic.dataclasses import dataclass
from typing import Optional
from abc import ABC, abstractmethod


@dataclass
class BaseEntity(ABC):
    @classmethod
    @abstractmethod
    def from_dict(cls, other: dict):
        ...

    @abstractmethod
    def dict(self):
        ...


@dataclass
class UserEntity(BaseEntity):
    id: Optional[str]
    name: str
    email: str
    avatar: str

    @classmethod
    def from_dict(cls, other: dict):
        return cls(
            id=other.get("id"),
            name=other.get("name"),
            email=other.get("email"),
            avatar=other.get("avatar"),
        )

内容的提问来源于stack exchange,提问作者Rodrigo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:55:22