2

Suppose I am using a library that provides the following classes:

class A:
    def __init__(self):
        print("A")

class B(A):
    def __init__(self):
        print("B")
        super().__init__()

I then define a mixin and a child class:

class C:
    def __init__(self):
        print("C")
        super().__init__()

class D(B, C): 
    def __init__(self):
        print("D")
        super().__init__()

The output of creating a new D is

D
B
A

My goal is for C's __init__ to also be called when D is initialized, without being able to modify B or A. Desired output would be

D
B
A
C

There are a variety of questions on using super with multiple inheritance, such as this one and this one. From this post, I understand that for the cooperative subclassing pattern to work, and C.__init__ to be called, B and A need to also call super, which would return C in the method resolution order. Hettinger recommends writing an adapter class to deal with this "non-cooperative" situation. I can write an adapter class for B, but unlike in Hettinger's example, B is the primary class I inherit from, rather than a mixin. My understanding is that I would have to "rewrite" every method that B implements in its adapter for this to work, which seems infeasible, especially if B is a large library class that might change behavior in the future.

So is there any way to get C's initialization behavior without being able to adapt B or A?

Micah Smith
  • 4,203
  • 22
  • 28

1 Answers1

2

The #1 golden rule of mixins:

Always inherit from the mixin first.


Proper mixins are designed to support multiple inheritance, i.e. they have a constructor with *args, **kwargs that calls super().__init__(*args, **kwargs) (or they don't have a constructor at all). A constructor like this is completely transparent and unnoticeable; it doesn't get in the way of the child class's constructor.

When the child class (D) calls super().__init__() it'll call the mixin's (C's) constructor, which will in turn call B's constructor. In other words, your problem is solved simply by reordering D's parent classes.

class D(C, B): 
    def __init__(self):
        print("D")
        super().__init__()

D()  # output: D C B A

If, for some reason, you absolutely have to call the constructors in the order D B A C, you should call the parent classes' constructors explicitly, without super():

class D(B, C): 
    def __init__(self):
        print("D")
        B.__init__(self)
        C.__init__(self)

D()  # output: D B A C
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149