I have a class split into mixins:
class MyObject(MyObjectFilesMixin, MyObjectProcessingMixin, ...):
def __init__(self, value):
self.value = self.preprocess(value)
A mixin looks like this:
class MyObjectFilesMixin:
def load_from_file(cls, filename):
return ...
Now I'd like to add typing to the class and mixins:
class MyObjectFilesMixin:
def load_from_file(cls, filename: str) -> MyObject:
return ...
class MyObjectProcessingMixin:
def preprocess(self: MyObject, value: bytes):
return value # logic is omitted
def append(self: MyObject, other: MyObject):
self.value += other.value
But it leads to cyclic links. Of course I can create some MyObjectBase
(following dependency inversion principle), so that MyObject
will also inherit this class, and mixins will use it as argument/return type, but this will lead to wrong types anyway. Is it possible to fix??
I miss so much header+source files from C++
I found how to do the trick without protocols or other unnecessary code, DRY! My solution is simple:
from typing import TYPE_CHECKING, Type
if TYPE_CHECKING:
from my_object import MyObject
MO = Type('MyObject')
class MyObjectFilesMixin:
def load_from_file(cls: MO, filename: str) -> 'MyObject':
# Now I'm able to access all the methods of MyObject by cls
# with correct IDE support and MyPy checking
return ...
class MyObjectProcessingMixin:
def preprocess(self: 'MyObject', value: bytes):
return value # logic is omitted
def append(self: 'MyObject', other: 'MyObject'):
self.value += other.value
However, I get another warning, probably because MyPy doesn't expect a child to be used as a type in parent:
Mypy: The erased type of self "Type[... MyObject]" is not a supertype of its class "Type[...MyObjectFilesMixin]"
But it's a little cost for the methods and types to be seen and understood correctly by IDE and MyPy!