I want to define a typing protocol for a custom mapping class. This class needs to be very similar to a MutableMapping
, except that it has a couple of additional methods beyond those those that collections.abc.MutableMapping
defines as abstract methods (specifically .copy()
), and I also want to specify in advance which types any implementations of the custom mapping class must use as keys and values.
After reading through PEP 544 I thought I should be able to do this:
from typing import Hashable, MutableMapping, Protocol, TypeVar
TVarMapProt = TypeVar("TVarMapProt", bound="VariableMappingProtocol")
class VariableMappingProtocol(MutableMapping[Hashable, int], Protocol):
"""
Protocol for the type behaviour of a class which
acts basically like a dict of int objects.
"""
def copy(self: TVarMapProt) -> TVarMapProt:
# TODO replace return type with Self if PEP 673 goes through
...
The idea being that in my code I can state that a type VariableMappingProtocol
is expected, and then the user would have to use their own class that was defined like this in order to avoid a typing error:
TCusVarMap = TypeVar("CusVarMap", bound="CustomVariableMapping")
class CustomVariableMapping:
"""Defines all the methods that a MutableMapping does, but mapping Hashables to ints"""
def __getitem__(self, key: Hashable) -> int:
# implementation goes here
...
# etc. for __setitem__, and so on...
def copy(self) -> TCusVarMap:
# implementation goes here
...
The problem is that when I run mypy
on the code defining VariableMappingProtocol
I get this error:
test.py:7: error: All bases of a protocol must be protocols
Found 1 error in 1 file (checked 1 source file)
If I delete MutableMapping
so that VariableMappingProtocol
only inherits from Protocol
then the error goes away. But then I'm not getting the structural typing I want for all the abstract methods from MutableMapping
.
So it seems the problem is that typing.MutableMapping
is not considered a Protocol
? But that's weird, especially as I can treat some of the other types in typing
as Protocols
, e.g. this example (from PEP 544)
from typing import Protocol, Sized
class SizedProtocol(Sized, Protocol):
"""
Protocol for the type behaviour of a class which has a __len__ method.
"""
...
which doesn't throw any mypy
errors.
How can I inherit from MutableMapping
as a Protocol
, and thus avoid the need to write out all the methods of MutableMapping
in my VariableMappingProtocol
definition?
typing.MutableMapping
is deprecated, as it was just a generic version ofcollections.abc.MutableMapping
, liketyping.Dict
was fordict
, and now the original types support being used generically so the version intyping
is superfluous. SoMutableMapping
is an abstract base class, not a protocol. – AnarthriaMutableMapping
into your protocol. By the way, I was not entirely correct:collections.abc.Sized
(for whichtyping.Sized
is a (apparently non-deprecated) alias) is also an abstract base class, but it is explicitly marked as okay as a base class for protocols. From what I can tell, the difference is becauseMutableMapping
implements mixin methods likepop
,update
andclear
. and it doesn't make sense for protocols to implement any methods. – Anarthria__iter__
. – Steveclass MyClass(Mapping, Protocol):
, and I got it working by removing theProtocol
, i.e, I'm interiting only fromMapping
(class MyClass(Mapping):
) and it seems it behaves like a Protocol, so maybe that's the way.. – Decalogue