Specifying type hints of overridden methods in generic collection

☆樱花仙子☆ 提交于 2019-12-22 10:16:23

问题


I have defined an abstract base class BaseRepository that acts as a collection of items with specified supertype Foo. The convenience classmethods in BaseRepository are annotated/type hinted to work with objects of type Foo. Here is a minimal example:

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

Now there are multiple static implementations (e.g. SubRepository) which are each supposed to work with their own type of items (like Bar), being subclasses of the original generic type 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()

The repositories are static, meaning that they are not instantiated but rather function as namespaces for proper access to items that I load from YAML configuration files. The main perk is that I can create one of these SubRepositories and simply override the deserialization method _load_items, and the resulting repository will have all convenience methods from the base class. As I need to ensure that all of these SubRepositories work with items Foo that have a specific interface in order for the BaseRepository methods function properly, the SubRepositories must work with items that inherit from Foo.

Strongly-typed languages like Java or C# have the concept of Generic Collections, where the elements in the subclassed collections all assume a specific type. Is the same possible with type hinting in Python? In particular, I would like the inherited get_item method in SubRepository to be hinted as Bar with minimal effort (not override it just for the sake of type hints). Optimally, the correct return value should be linted by PyCharm.

Currently, even though SubRepository holds Bar items, my autocompletion in PyCharm only shows me members of Foo.

I read about typing.Generic and TypeVar, but I'm unsure how to use them in this case.


回答1:


You're programming to an interface, so only Foo members are exposed.

from typing import get_type_hints
print(get_type_hints(SubRepository.get_item))

Output:

{'return': <class '__main__.Foo'>}

A generic collection will expose the generic type's members.

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()

Return type

print(get_type_hints(SubRepository.get_item))

Passes the buck

{'return': ~T}

Autocompletion will now show members of Bar.



来源:https://stackoverflow.com/questions/49583643/specifying-type-hints-of-overridden-methods-in-generic-collection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!