我在输入 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]
正如其他人所说,将 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"))
希望这有帮助!