How do I correctly add type-hints to Mixin classes?
One approach I saw in this question is type hinting the self
attribute. Together with Union
from the typing package, you are able to use the attributes from a class which is used together with your mixin, while still having correct type hinting for own attributes:
from typing import Union
class AdditionMixin:
def add(self: Union[MyBaseClass, 'AdditionMixin'], b: int) -> int:
return self.value + b
class MyBaseClass:
def __init__(self, value: int):
self.value = value
Downside is that you have to add the hint to every method, which is kind of cumbersome.
In addition to Campi's answer about the mypy's recommendation of typing mixins with Protocol
:
An alternative to typing the methods' self
s is just inheriting the protocol.
from typing import Protocol
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
class MultiplicationMixin(HasValueProtocol):
def multiply(self, m: int) -> int:
return self.value * m
class AdditionMixin(HasValueProtocol):
def add(self, b: int) -> int:
return self.value + b
class MyClass(MultiplicationMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
Additionally, if you are TYPE_CHECKING
a Protocol
, and given that you cannot forward reference a parent class (i.e. passing the parent class as a string literal), a workaround would be:
from typing import Protocol, TYPE_CHECKING
if TYPE_CHECKING:
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
else:
class HasValueProtocol: ...
class MultiplicationMixin(HasValueProtocol):
def multiply(self, m: int) -> int:
return self.value * m
...
I've tested it on my machine, hope it will also work for you:
class MultiplicatorMixin:
value = None # type: int
def multiply(self, m: int) -> int:
return self.value * m
class AdditionMixin:
value = None # type: int
def add(self, b: int) -> int:
return self.value + b
class MyClass(MultiplicatorMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
instance = MyClass(10)
print(instance.add(2))
print(instance.multiply(2))
For reference, mypy recommends to implement mixins through a Protocol
(documentation here).
It works with mypy >= 750.
from typing import Protocol
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
class MultiplicationMixin:
def multiply(self: HasValueProtocol, m: int) -> int:
return self.value * m
class AdditionMixin:
def add(self: HasValueProtocol, b: int) -> int:
return self.value + b
class MyClass(MultiplicationMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
The Protocol
base class is provided in the typing_extensions
package for Python 2.7 and 3.4-3.7.