0

I'm struggling to have the right types in a code implying two related classes and two related mixins, which can be applied on these classes.

Here is the minimal code I have to demonstrate my problem and expectations.

Three things I need help on:

  1. The last statement TestCImpl2().mixin_func2() does not return a compatible type with what I'm expecting. Any idea to fix that?
  2. The current code requires to repeat a generic type on the mixin: class TestCImpl2(MixinOnMyBase2[MyBase2[TestCImpl1]], MyBase2[TestCImpl1]). I'm pretty sure there is a solution which does not involve having to do this.
  3. I currently use a Protocol, but I'm not sure it is required here.

Note: I know I'm over-engineering it, but I take the occasion to go further in Python typing system.

from typing import Protocol, Self

# --- Not in my control ---


# A simple class
class Base1:
    pass


# A class which require a generic type
class Base2[T]:
    pass


# --- My base classes and mixins ---


# My simple base class with a custom function which return the implementation class type
class MyBase1(Base1):
    def base_func1(self) -> Self:
        return self


# My generic-type base class with a custom function which return the implementation class type
# I require the generic type to be based on MyBase1 and default to MyBase1 when not specified
# The class has a property of generic-type MyBase1 and a function returning implementation class type
class MyBase2[MyT: MyBase1 = MyBase1](Base2[MyT]):
    object1: MyT

    def base_func2(self) -> Self:
        return self


# A mixin I want sometimes to apply on MyBase1 based classes
class MixinOnMyBase1:
    def mixin_func1(self) -> Self:
        return self


# A Protocol just to type-hint a generic type with object1 and base_func2,
# I'm not sure it is required
class MixinOnMyBase2Protocol[T](Protocol):
    @property
    def object1(self) -> MixinOnMyBase1: ...

    def base_func2(self, *args, **kwargs) -> T: ...


# A mixin I want to sometimes to apply on MyBase2 based classes, which works by pair with MyBase1
class MixinOnMyBase2[T]:
    def mixin_func2(self: MixinOnMyBase2Protocol[T]) -> T:
        self.object1.mixin_func1()
        return self.base_func2()


# --- My expected final implementations ---


# Test A
class TestAImpl1(MyBase1):
    pass


class TestAImpl2(MyBase2):
    pass


a: MyBase2 = TestAImpl2().base_func2()


# Test B
class TestBImpl1(MyBase1):
    pass


class TestBImpl2(MyBase2[TestBImpl1]):
    pass


b: MyBase2[TestBImpl1] = TestBImpl2().base_func2()
c: TestBImpl2 = TestBImpl2().base_func2()


# Test C


class TestCImpl1(MixinOnMyBase1, MyBase1):
    pass


# I find ugly to have to repeat MyBase2[TestCImpl1] on the mixin
class TestCImpl2(MixinOnMyBase2[MyBase2[TestCImpl1]], MyBase2[TestCImpl1]):
    pass


d: MyBase2[TestCImpl1] = TestCImpl2().mixin_func2()
# Here mypy complain:
# Incompatible types in assignment (expression has type "MyBase2[TestCImpl1]", variable has type "TestCImpl2")
# Mypy assignment
e: TestCImpl2 = TestCImpl2().mixin_func2()

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.