3

Edit: I am on Python 3.

I have a class X that extends both Y and Z:

class X(Y, Z):
    def __init__(self):
    # Call Y's __init__
    # Call Z's __init
    pass

I need to call __init__ on both Y and Z from the __init__ on X. What would be the best way to do this?

4
  • Is this Python 2 or 3? Commented Sep 9, 2016 at 15:32
  • I am on Python 3. Commented Sep 9, 2016 at 15:34
  • That's another one reason, why multiple inheritance - is bad, so how you can ask about best practices in such bad practice? Commented Sep 9, 2016 at 15:36
  • 1
    OK, I removed the part about best practices. Now what would be your suggestion? Commented Sep 9, 2016 at 15:38

2 Answers 2

6

This depends entirely on whether Y and Z were designed to work cooperatively with multiple inheritance. If they are, then you should be able to just use super:

class X(Y, Z):
    def __init__(self):
        super().__init__()

When I say that Y and Z need to be designed to work cooperatively with multiple inheritance, I mean that they must also call super in their __init__ methods:

class Y:
    def __init__(self):
        print('Y.__init__')
        super().__init__()

class Z:
    def __init__(self):
        print('Z.__init__')
        super().__init__()

They should also have some strategy for handling different arguments since (particularly the constructor) of a class frequently differ in which keyword arguments that they accept. At this point, it's worth reading Super considered super! and Super considered harmful to understand some common idioms and pitfalls regarding super.

If you don't know whether Y and Z were designed for cooperative multiple inheritance, then the safest recourse is to assume that they weren't.


If the classes aren't designed to work with cooperative multiple inheritance, then you need to start stepping lightly (you're in dangerous waters). You can call each __init__ individually:

class X(Y, Z):
    def __init__(self):
        Y.__init__(self)
        Z.__init__(self)

But really it's probably better to not use multiple inheritance and instead choose a different paradigm (e.g. composition).

Sign up to request clarification or add additional context in comments.

8 Comments

In my case, the parents are - threading.Thread and logging.Handler - how do I check if they are designed to work cooperatively in multiple inheritance?
As far as I can tell, threading.Thread does not.
@masnun The question you need to ask yourself is: why do you want to inherit from both of them? You are mixing unrelated concepts. It would be cleaner to use a class with both of them as fields (or some other clean pattern).
@mgilson I guess Handler also doesn't use super. I can see it calls Filterer.__init__(self) in it's own __init__. Thanks for the explanation.
Let's assume that Y also has a base-class (Q) and Y calls super().__init__() to make sure that it (implicitly) calls Q.__init__(). Depending on the method resolution order, Y's super().__init__() might actually call Z.__init__ instead of Q.__init__. If Z.__init__ also supers, then you'll call Z.__init__ twice (once here and once explicitly later). If Z.__init__ doesn't super, then you'll likely skip Q.__init__ entirely.
|
4

Generally there is no other way then to call Y.__init__(self) and Z.__init__(self) directly. It's the only way to ensure that both constructors are called at least once.

However in some weird (or normal, depending on how you look at it) cases this can lead to very unintuitive results where base constructors are called multiple times:

class Y:
    def __init__(self):
        print('Y')
        super().__init__()

class Z:
    def __init__(self):
        print('Z')
        super().__init__()

class X(Y, Z):
    def __init__(self):
        print('X')
        Y.__init__(self)
        Z.__init__(self)

X()

Results in

X
Y
Z
Z

So in that case super().__init__() in X (instead of both .__init__() calls) seems natural and will work correctly. However it will fail if Y and Z don't call super().__init__() as well. In that case you will only call one of the constructors.

So generally there is no way to solve your issue correctly without knowing implementation of base classes.

YARtAMI = Yet Another Reason to Avoid Multiple Inheritance

3 Comments

Thanks for the detailed explanation. In my case none of the parent classes have any common ancestor.
@masnun They don't have to have. I've updated the answer.
Is there any explanation for understanding why the first call super().__init__() in Y triggers the Z.__init__()??? I understood Y and Z inherit from object, so I can't understand why the first 'Z' print on screen

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.