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

如何为返回带类型类方法的Python工厂函数添加类型注解

为返回带类型化类方法的Python工厂函数添加正确的类型注解

我在为返回带有类型化类方法的Python工厂函数添加类型注解时遇到了困难。具体来说,我有一个Factory()工厂函数,用于创建带有.create()类方法的CRUD类,它接收两个参数:

  • model:对应数据库表的SQLAlchemy模型类
  • schema:Pydantic schema类

初始实现代码如下:

from typing import Type, TypeVar

from pydantic import BaseModel
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


SqlModel = TypeVar("SqlModel", bound=Base)
PydanticSchema = TypeVar("PydanticSchema", bound=BaseModel)


def Factory(model: Type[SqlModel], schema: Type[PydanticSchema]):
    class Foo:
        @classmethod
        def create(cls, data: schema) -> model:
            db_model = model(**data.model_dump())
            return db_model

    return Foo


class MyModel(Base):
    name: Mapped[str] = mapped_column(primary_key=True)


class MySchema(BaseModel):
    name: str


MyFactory = Factory(MyModel, MySchema)
bar = MyFactory.create(MySchema(name="bar"))

我希望通过Factory实例化的类,其.create()方法的类型注解能和传入的model、schema类完全匹配。但当前实现存在问题:

  • 使用TypeVar和Type时,参数data的类型显示为MySchema@Factory,返回类型为MyModel@Factory,无法关联到原始类的类型和方法,不符合预期
  • 不使用TypeVar时,两者类型都会变成Any

请问如何正确添加类型注解,使得调用MyFactory = Factory(MyModel, MySchema)后,MyFactory.create()的参数类型为MySchema,返回类型为MyModel?


编辑补充:我尝试用泛型和双重TypeVar实现了功能,但不确定是否正确或是否滥用了类型系统,希望得到反馈:

SqlModel = TypeVar("SqlModel", bound=Base)
PydanticSchema = TypeVar("PydanticSchema", bound=BaseModel)


def Factory(model: Type[SqlModel], schema: Type[PydanticSchema]):
    FooSqlModel = TypeVar("FooSqlModel", bound=model)
    FooSchema = TypeVar("FooSchema", bound=schema)

    class Foo(Generic[FooSqlModel, FooSchema]):
        @classmethod
        def create(cls, data: FooSchema) -> FooSqlModel:
            db_model = model(**data.model_dump())
            return db_model

    return Foo[model, schema]

正确的类型注解方案

不需要在工厂函数内部额外定义TypeVar,只需让返回的类继承泛型,并通过外层的TypeVar绑定类型即可。核心是让工厂函数返回携带具体泛型参数的类实例,直接关联传入的model和schema类型。

修改后的代码如下:

from typing import Type, TypeVar, Generic

from pydantic import BaseModel
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


# 定义顶层TypeVar,用于绑定泛型类型
SqlModel = TypeVar("SqlModel", bound=Base)
PydanticSchema = TypeVar("PydanticSchema", bound=BaseModel)


def Factory(model: Type[SqlModel], schema: Type[PydanticSchema]) -> Type[Generic[SqlModel, PydanticSchema]]:
    class Foo(Generic[SqlModel, PydanticSchema]):
        @classmethod
        def create(cls, data: PydanticSchema) -> SqlModel:
            db_model = model(**data.model_dump())
            return db_model

    return Foo


class MyModel(Base):
    __tablename__ = "my_model"  # 补充表名避免SQLAlchemy警告
    name: Mapped[str] = mapped_column(primary_key=True)


class MySchema(BaseModel):
    name: str


MyFactory = Factory(MyModel, MySchema)
# 此时IDE会正确提示:create方法接收MySchema类型参数,返回MyModel类型
bar = MyFactory.create(MySchema(name="bar"))

方案说明

  1. 顶层TypeVar绑定:外层定义的SqlModel和PydanticSchema直接绑定到内部Foo泛型类,类型检查器可追踪到传入的具体类类型。
  2. 明确返回类型:工厂函数的返回类型标注为Type[Generic[SqlModel, PydanticSchema]],清晰告知类型检查器返回的是携带具体泛型参数的类。
  3. 冗余代码移除:内部额外定义TypeVar属于冗余操作,外层的TypeVar已能完成类型绑定,简化后更符合类型系统设计逻辑。

对尝试方案的反馈

你之前的实现能正常运行,但存在冗余的TypeVar定义:内部的FooSqlModel和FooSchema完全可以被外层的TypeVar替代,无需额外绑定到model和schema实例。这种写法不算“滥用类型系统”,但属于不必要的复杂写法,简化后更清晰易维护。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:10:55