python - 将泛型子类的类型推断为将自身作为类型

标签 python mypy python-typing

我正在为外部 ORM 库制作 stub ,我遇到了一个问题,但我不确定如何克服。因此,下面的示例在技术上通过了 mypy 检查,但只是在期望库用户在类声明期间繁琐地重复自己之后。

# 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*]"

这正是您所期望的,但是这只有效,因为用户必须将类名作为每个类定义上的类型传递。省略类型导致其具有 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]"

所以我的问题是如何使这种类型推断对用户不可见?

最佳答案

这是因为你把它变成了一个泛型类,它不应该是泛型类,它本质上是一个泛型函数。只需使用以下内容:

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

请注意,有一个 PEP 可以删除 TypeVar 样板:

https://www.python.org/dev/peps/pep-0673/

关于python - 将泛型子类的类型推断为将自身作为类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70342345/

相关文章:

python - 将刚刚安装的包导入现有的 python session

python - Sqlite 插入不适用于 python

python - 如何在 Python 中表达泛型对象的扩展子类型?

python - 有没有办法在 Bokeh 中为非选择字形设置多个不透明度?

python - RequestsDependencyWarning : urllib3 (1. 25.2) 或 chardet (3.0.4) 与支持的版本不匹配!使固定

python - 如果我将默认设置为 None 可以省略 Optional 吗?

python - 子类中返回值的类型提示

python-3.x - Typing.Protocol 类 `__init__` 方法在显式子类型构造期间未调用

mypy - 是否可以运行 mypy pre-commit 而不会失败?

python-3.x - 在 MyPy 中使用 TypeVar 输入带参数的装饰器会产生预期的无人居住类型