SuperDisk
SuperDisk

Reputation: 2840

Python's Multiple Inheritance: Picking which super() to call

In Python, how do I pick which Parent's method to call? Say I want to call the parent ASDF2's __init__ method. Seems like I have to specify ASDF1 in the super()..? And if I want to call ASDF3's __init__, then I must specify ASDF2?!

>>> class ASDF(ASDF1, ASDF2, ASDF3):
...     def __init__(self):
...         super(ASDF1, self).__init__()
            
>>> ASDF()
# ASDF2's __init__ happened

>>> class ASDF(ASDF1, ASDF2, ASDF3):
...     def __init__(self):
...         super(ASDF2, self).__init__()

>>> ASDF()
# ASDF3's __init__ happened

Seems bonkers to me. What am I doing wrong?

Upvotes: 67

Views: 43592

Answers (4)

Nathan Muncy
Nathan Muncy

Reputation: 1

Now in python3, just control which method super() is searching in mro.

class _TestA:
    def __init__(self, foo: str):
        self._print = "testA-" + foo


class _TestB:
    def __init__(self, foo: str):
        self._print = "testB-" + foo


class TestAB(_TestA, _TestB):
    def __init__(self, foo: str):
        self._foo = foo
        if foo == "A":
            self._runA()
        else:
            self._runB()
        print(self._print)

    def _runA(self):
        print("Running _TestA")
        super().__init__(self._foo)

    def _runB(self):
        print("Running _TestB")
        super(_TestA, self).__init__(self._foo)


ta = TestAB("A") 
tb = TestAB("B")

This gives

Running _TestA
testA-A
Running _TestB
testB-B

Upvotes: 0

Falmarri
Falmarri

Reputation: 48605

That's not what super() is for. Super basically picks one (or all) of its parents in a specific order. If you only want to call a single parent's method, do this

class ASDF(ASDF1, ASDF2, ASDF3):
    def __init__(self):
        ASDF2.__init__(self)

Upvotes: 100

Christian Long
Christian Long

Reputation: 11544

You are passing ASDF1 (one of your parent classes) as the first argument to super(). Don't do that. Instead, you should pass ASDF as the first argument to super().

Python 2.7 documentation for super()

To quote the Python 2.7 docs, a typical superclass call looks like this:

class C(B):
    def method(self, arg):
        super(C, self).method(arg)

Notice that the first argument to super here is C, which is the current class. Don't pass B (the parent class) to super().

When determining the Method Resolution Order (MRO) super skips the class that is passed in as its first argument, and starts looking at that class' parents and siblings. So, when you passed ASDF1 as the first argument to super(), it skipped over ASDF1 and started its search with ASDF2. That's why ASDF2's __init__ was called.


In Python 3, you don't have to pass the current class any more.

class C(B):
    def method(self, arg):
        super().method(arg)    # This does the same thing as:
                               # super(C, self).method(arg)
                               # (Python 3 only)

Upvotes: 12

Marcin
Marcin

Reputation: 49886

super calls the next method in the method resolution order. In a linear inheritance tree, that will be method from the immediately parent class.

Here, you have three parents, and the next __init__ method from ASDF1's perspective is that of ASDF2. In general, the safe thing to do is to pass the first class in the inheritance list to super unless you know why you want to do something else.

The point of all of this is to relieve you of having to explicitly pick which __init__ methods to call. The question here is why you don't want to call all of the superclass __init__ methods.

There's a fairly substantial literature on the use of super in python, and I recommend that you google the topic, and read what results.

Upvotes: 12

Related Questions