如何输入一个 Python 工厂函数,该函数返回带有类型化类方法的类

问题描述 投票:0回答:1

我在输入 Python 工厂函数时遇到问题,该函数返回具有正确类型的类方法的类。

举一个具体的例子,我有一个工厂函数 Factory(),它使用类方法 .create() 创建一个 CRUD 类。 Factory() 函数有两个参数:

  • model
    :一个SQLAlchemy模型类,代表一个表
  • schema
    :Pydantic 模式类
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"))

我想在

.create()
方法的主体及其类型中使用这两个类,以便当我从 Factory 函数实例化一个类时,
.create()
方法将使用传递给的模型和模式类进行类型化
Factory

示例中:

  • 使用
    TypeVar
    Type
    ,我得到的数据类型为
    MySchema@Factory
    ,返回类型为
    MyModel@Factory
    ,但这似乎不正确,因为它没有链接到原始类类型及其方法
  • 如果没有
    TypeVar
    ,则两者都输入为
    Any

如何输入此内容,以便在调用

MyFactory = Factory(MyModel, MySchema)
时,
MyFactory.create()
具有正确的参数注释
MySchema
和返回类型
MyModel

EDIT 2024-11-08 09:43:00 UTC

我想我可以在泛型和双重类型

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]
python python-typing mypy
1个回答
0
投票

课堂方式

正如其他人所说,将 create 嵌入为 Factory 的方法确实是可行的方法 - 您仍然可以获得相同的良好语法,它修复了所有打字问题,而且更短:

from typing import Generic, TypeVar, Generic

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)

class Factory(Generic[SqlModel, PydanticSchema]):
    def __init__(self, model: type[SqlModel], schema: type[PydanticSchema]) -> None:
        self._model = model
        self._schema = schema

    def create(self, data: PydanticSchema) -> SqlModel:
        db_model = self._model(**data.model_dump())
        return db_model


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"))

协议方式

如果你真的真的需要它成为一个单独的类......它可以通过

Protocol
来完成:

from typing import Generic, Protocol, TypeVar

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


class Base(DeclarativeBase):
    pass


SqlModel = TypeVar("SqlModel", bound=Base, covariant=True)
PydanticSchema = TypeVar("PydanticSchema", bound=BaseModel, contravariant=True)

class FooLike(Protocol, Generic[PydanticSchema, SqlModel]):
    @classmethod
    def create(cls, data: PydanticSchema) -> SqlModel:
        ...

def Factory(model: type[SqlModel], schema: type[PydanticSchema]) -> "type[FooLike[PydanticSchema, SqlModel]]":
    class Foo:
        @classmethod
        def create(cls, data: PydanticSchema) -> SqlModel:
            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"))

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.