指定泛型集合中重写方法的类型提示

Jos*_*zer 4 python generics collections inheritance types

我定义了一个抽象基类BaseRepository,它充当具有指定 supertype 的项目的集合Foo。中的便利类方法BaseRepository被注释/类型暗示用于类型的对象Foo。这是一个最小的例子:

from abc import ABCMeta, abstractmethod
NoReturn = None

class Foo(object):
    pass  # simple data holding object


class BaseRepository(object, metaclass=ABCMeta):

    # May be filled with subtypes of `Foo` later
    _items = None  # type: List[Foo]

    @classmethod
    def get_item(cls) -> Foo:
        return cls._items[0]

    @classmethod
    @abstractmethod
    def _load_items(cls) -> NoReturn:
        pass
Run Code Online (Sandbox Code Playgroud)

现在有多个静态实现(例如SubRepository),每个实现都应该使用它们自己的项目类型(例如Bar),它们是原始泛型类型的子类Foo

class Bar(Foo):
    pass  # Must implement Foo in order for BaseRepository's methods to work

def load_some_bars():
    return [Bar(),Bar()]

class SubRepository(BaseRepository):
    # Inherits `get_item` from BaseRepository

    @classmethod
    def _load_items(cls) -> NoReturn:
        cls._items = load_some_bars()
Run Code Online (Sandbox Code Playgroud)

存储库是静态的,这意味着它们没有被实例化,而是作为命名空间来正确访问我从 YAML 配置文件加载的项目。主要好处是我可以创建其中之一SubRepositories并简单地覆盖反序列化方法_load_items,并且生成的存储库将具有基类中的所有便利方法。由于我需要确保所有这些SubRepositories都与Foo具有特定接口的项目一起使用以使BaseRepository方法正常运行,因此SubRepositories必须与从Foo.

Java 或 C# 等强类型语言具有Generic Collections的概念,其中子类集合中的元素都采用特定类型。Python 中的类型提示是否可能相同?特别是,我希望以最小的努力来暗示继承的get_item方法(而不是仅仅为了类型提示而覆盖它)。最佳情况下,正确的返回值应该由 PyCharm 检测。SubRepositoryBar

目前,即使SubRepository持有Bar项目,我在 PyCharm 中的自动完成也只向我显示Foo.

我阅读了typing.Genericand TypeVar,但我不确定在这种情况下如何使用它们。

Fun*_*unk 5

您正在对接口进行编程,因此只Foo公开成员。

from typing import get_type_hints
print(get_type_hints(SubRepository.get_item))
Run Code Online (Sandbox Code Playgroud)

输出:

{'return': <class '__main__.Foo'>}
Run Code Online (Sandbox Code Playgroud)

泛型集合将公开泛型类型的成员。

from typing import TypeVar, Generic, get_type_hints
from abc import ABCMeta, abstractmethod
NoReturn = None

# type variable with an upper bound
T = TypeVar('T', bound=Foo)

class BaseRepository(Generic[T], metaclass=ABCMeta):
    _items = None  # type: List[T]

    @classmethod
    def get_item(cls) -> T:
        return cls._items[0]

    @classmethod
    @abstractmethod
    def _load_items(cls) -> NoReturn:
        pass

class SubRepository(BaseRepository[Bar]):
    # Inherits `get_item` from BaseRepository

    @classmethod
    def _load_items(cls) -> NoReturn:
        cls._items = load_some_bars()
Run Code Online (Sandbox Code Playgroud)

返回类型

print(get_type_hints(SubRepository.get_item))
Run Code Online (Sandbox Code Playgroud)

推卸责任

{'return': ~T}
Run Code Online (Sandbox Code Playgroud)

自动完成现在将显示Bar.