我有一个泛型类 GenericItem,它有一个名称和一个类型变量,我想将其用作类型提示。
from typing import Generic, TypeVar
from pydantic import BaseModel
T = TypeVar("T", bound=BaseModel)
class GenericItem(Generic[T]):
def __init__(self, name: str, type_that_i_need_in_typehint: type[T] | None) -> None:
self.name: str = name
self.type_: type[T] | None = type_that_i_need_in_typehint
GenericItems 存储在另一个类中:GenericItemStore,即使用字典来存储 GenericItem 的名称/项目对。它在示例中进行了简化,实际的类存储通用 Endpoint 对象,并在底层具有 httpx 功能来发出 API 请求。
class GenericItemStore:
def __init__(self, items: list[GenericItem[T]]) -> None:
self.generic_items_registry: dict[str, GenericItem[T]] = {}
self._configure_registry(items)
def _configure_registry(self, items: list[GenericItem[T]]) -> None:
for item in items:
self.generic_items_registry[item.name] = item
def get_from_registry(self, name: str) -> GenericItem[T]:
return self.generic_items_registry[name]
def some_method_that_need_typehints(self, name: str, actual_instance_of_type: T) -> None:
generic_item: GenericItem[T] = self.get_from_registry(name)
# there goes other processing
return None
当我使用 GenericItemStore's *some_method_that_need_typehints *我想为第二个参数使用 typehint,但因为函数签名是静态定义的,并且实际的通用项是在函数体中(动态)检索的,所以它不会类型提示我想要传递的真实类型。
class BaseModelSubclass(BaseModel):
some_param: int
generic_item_store = GenericItemStore(
items=[
GenericItem(name="First", type_that_i_need_in_typehint=BaseModelSubclass),
GenericItem(name="Second", type_that_i_need_in_typehint=None),
]
)
# actual_instance_of_type is type hinted by PyCharm as T because it's a static func signature
# I want it to be typehinted as BaseModelSubclass but don't know how and if it's even possible
generic_item_store.some_method_that_need_typehints("First", actual_instance_of_type=...)
有没有办法以某种方式输入这个参数?当前的 python 打字系统是否有可能,或者我只是理解错误? 也许我应该完全改变构建这个应用程序的方式?
我尝试过:
使用自定义通用注册表而不是字典
mypy.cast
如有任何帮助,我们将不胜感激。
有没有办法以某种方式输入这个参数?当前的 python 打字系统是否有可能,或者我只是理解错误?也许我应该完全改变构建这个应用程序的方式?
你是对的,这(目前)还不可能。目前,除了
dict
之外,无法定义静态形状的 TypedDict
,并且 TypedDict
只能使用 class D(TypedDict)
/D = TypedDict('D', ...)
来定义,其中所有键都必须进行硬编码。
除此之外,您也无法像在 TypeScript 中使用
Literal[]
一样派生出由 TypedDict
的所有键组成的 keyof
类型。
如果你真的下定决心,这已经是你能做到的最好的了,但此时你最好在任何地方都使用
typing.cast()
:
class GenericItem(Generic[T]):
def __init__(self, name: str) -> None:
self.name: str = name
class Items(TypedDict):
First: GenericItem[BaseModelSubclass]
Second: GenericItem[None]
class GenericItemStore:
registry: Items
def __init__(self, items: Items) -> None: ...
@overload
def method(self, name: Literal['First'], instance: BaseModelSubclass) -> None: ...
@overload
def method(self, name: Literal['Second'], instance: None) -> None: ...
def method(self, name: Literal['First', 'Second'], instance: Any) -> None:
generic_item = self.registry[name]
reveal_type(generic_item) # GenericItem[BaseModelSubclass] | GenericItem[None]
generic_item_store = GenericItemStore(
items = Items(
First = GenericItem[BaseModelSubclass]('First'),
Second = GenericItem[None]('Second'),
)
)
generic_item_store.method("First", BaseModelSubclass()) # fine
generic_item_store.method("First", None) # error
generic_item_store.method("Second", None) # fine
generic_item_store.method("Second", BaseModelSubclass()) # error