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' selfs 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.