问题
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