Search code examples
pythongenericspython-typingmypy

Declare a generic Mapping subclass with Python type annotations?


I'm trying to add generic type annotations to a Mapping subclass in Python 3.4:

from typing import Mapping, TypeVar, Iterator, Dict

K = TypeVar('K')
V = TypeVar('V')


class M(Mapping[K, V]):
    def __init__(self) -> None:
        self.d = dict()     # type: Dict[K, V]

    def __getitem__(self, item: K) -> V:
        return self.d[item]

    def __len__(self) -> int:
        return len(self.d)

    def __iter__(self) -> Iterator[K]:
        return iter(self.d)


# Also errors, but less
# d = dict()  # type: Mapping[K, V]

What am I doing wrong, and why doesn't mypy give a more useful error message?

$ python -V; mypy -V
Python 3.4.3+
mypy 0.470

$ mypy map.py
map.py:7: error: Invalid type "map.K"
map.py:7: error: Invalid type "map.V"
map.py:9: error: Invalid type "map.K"
map.py:9: error: Invalid type "map.V"

Solution

  • Seems like you have to add Generic[_K, _V] as an explicit base class.

    from typing import Mapping, TypeVar, Iterator, Dict, Generic
    
    _K = TypeVar('_K')
    _V = TypeVar('_V')
    
    
    class M(Generic[_K, _V], Mapping[_K, _V]):
        def __init__(self) -> None:
            self.d: Dict[_K, _V] = dict()
    
        def __getitem__(self, item: _K) -> _V:
            return self.d[item]
    
        def __len__(self) -> int:
            return len(self.d)
    
        def __iter__(self) -> Iterator[_K]:
            return iter(self.d)
    

    And before you ask, mypy does not have a functioning notion of a Hashable constraint for keys (as of version 0.470). [1] [2]

    Note: It is adviced to name your TypeVar with a leading _, to mark it as private. It is not supposed to be imported and used outside your package