将泛型子类的类型推断为具有自身类型
Infer Type of a Generic subclass as having itself as Type
我正在为外部 ORM 库制作存根,我遇到了一个我不确定如何克服的问题。因此,下面的示例在技术上通过了 mypy 检查,但只是在期望库用户在 class 声明期间单调乏味地重复自己之后。
# Library stubs:
from typing import Generic, TypeVar, Type, Any, Optional
from collections.abc import Collection, Sequence
from abc import ABC
T = TypeVar('T', bound='BaseItem')
K = TypeVar('K')
class ItemSet(Generic[K]):
def get_or_none(self, **kwargs: Any) -> Optional[K]: ...
def first(self) -> K: ...
def all(self) -> Collection[K]: ...
def order_by(self, *args: Any) -> Sequence[K]: ...
class BaseItem(ABC, Generic[T]):
@classmethod
def set(cls: Type[T]) -> ItemSet[T]: ...
# User's model:
from library import BaseItem
class FooItem(BaseItem['FooItem']):
name: str
class BarItem(BaseItem['BarItem']):
size: float
class BazItem(BaseItem['BazItem']):
id_: int
reveal_type(FooItem.set())
reveal_type(FooItem.set().all())
这会生成以下输出:
main.py:32: note: Revealed type is "__main__.ItemSet[__main__.FooItem*]"
main.py:33: note: Revealed type is "typing.Collection[__main__.FooItem*]"
这正是您所期望的,但这只有效,因为用户必须将 class 名称作为类型传递给 every class定义。类型的省略导致它具有 Any
类型
class FooItem(BaseItem):
name: str
main.py:32: note: Revealed type is "__main__.ItemSet[Any]"
main.py:33: note: Revealed type is "typing.Collection[Any]"
所以我的问题是如何使这种类型推断对用户不可见?
因为你把它变成了泛型class,它不应该是泛型class,它本质上是一个泛型函数。只需使用以下内容:
from typing import Generic, TypeVar, Type, Any, Optional
from collections.abc import Collection, Sequence
from abc import ABC
T = TypeVar('T', bound='BaseItem')
K = TypeVar('K')
class ItemSet(Generic[K]):
def get_or_none(self, **kwargs: Any) -> Optional[K]: ...
def first(self) -> K: ...
def all(self) -> Collection[K]: ...
def order_by(self, *args: Any) -> Sequence[K]: ...
class BaseItem(ABC):
@classmethod
def set(cls: Type[T]) -> ItemSet[T]: ...
class FooItem(BaseItem):
name: str
class BarItem(BaseItem):
size: float
class BazItem(BaseItem):
id_: int
reveal_type(FooItem.set())
reveal_type(FooItem.set().all())
这是 MyPy 的想法(注意,为简洁起见,我将所有内容都放在一个名为 test.py 的模块中):
(py39) Juans-MacBook-Pro:~ juan$ mypy test.py
test.py:29: note: Revealed type is "test.ItemSet[test.FooItem*]"
test.py:30: note: Revealed type is "typing.Collection[test.FooItem*]"
请注意,此特定情况已解决 here in the PEP-484 spec
注意,有一个删除 TypeVar
样板的 PEP:
我正在为外部 ORM 库制作存根,我遇到了一个我不确定如何克服的问题。因此,下面的示例在技术上通过了 mypy 检查,但只是在期望库用户在 class 声明期间单调乏味地重复自己之后。
# Library stubs:
from typing import Generic, TypeVar, Type, Any, Optional
from collections.abc import Collection, Sequence
from abc import ABC
T = TypeVar('T', bound='BaseItem')
K = TypeVar('K')
class ItemSet(Generic[K]):
def get_or_none(self, **kwargs: Any) -> Optional[K]: ...
def first(self) -> K: ...
def all(self) -> Collection[K]: ...
def order_by(self, *args: Any) -> Sequence[K]: ...
class BaseItem(ABC, Generic[T]):
@classmethod
def set(cls: Type[T]) -> ItemSet[T]: ...
# User's model:
from library import BaseItem
class FooItem(BaseItem['FooItem']):
name: str
class BarItem(BaseItem['BarItem']):
size: float
class BazItem(BaseItem['BazItem']):
id_: int
reveal_type(FooItem.set())
reveal_type(FooItem.set().all())
这会生成以下输出:
main.py:32: note: Revealed type is "__main__.ItemSet[__main__.FooItem*]"
main.py:33: note: Revealed type is "typing.Collection[__main__.FooItem*]"
这正是您所期望的,但这只有效,因为用户必须将 class 名称作为类型传递给 every class定义。类型的省略导致它具有 Any
类型
class FooItem(BaseItem):
name: str
main.py:32: note: Revealed type is "__main__.ItemSet[Any]"
main.py:33: note: Revealed type is "typing.Collection[Any]"
所以我的问题是如何使这种类型推断对用户不可见?
因为你把它变成了泛型class,它不应该是泛型class,它本质上是一个泛型函数。只需使用以下内容:
from typing import Generic, TypeVar, Type, Any, Optional
from collections.abc import Collection, Sequence
from abc import ABC
T = TypeVar('T', bound='BaseItem')
K = TypeVar('K')
class ItemSet(Generic[K]):
def get_or_none(self, **kwargs: Any) -> Optional[K]: ...
def first(self) -> K: ...
def all(self) -> Collection[K]: ...
def order_by(self, *args: Any) -> Sequence[K]: ...
class BaseItem(ABC):
@classmethod
def set(cls: Type[T]) -> ItemSet[T]: ...
class FooItem(BaseItem):
name: str
class BarItem(BaseItem):
size: float
class BazItem(BaseItem):
id_: int
reveal_type(FooItem.set())
reveal_type(FooItem.set().all())
这是 MyPy 的想法(注意,为简洁起见,我将所有内容都放在一个名为 test.py 的模块中):
(py39) Juans-MacBook-Pro:~ juan$ mypy test.py
test.py:29: note: Revealed type is "test.ItemSet[test.FooItem*]"
test.py:30: note: Revealed type is "typing.Collection[test.FooItem*]"
请注意,此特定情况已解决 here in the PEP-484 spec
注意,有一个删除 TypeVar
样板的 PEP: