[Tutor] super constructor usage

Steven D'Aprano steve at pearwood.info
Wed Mar 29 21:13:38 EDT 2017

On Wed, Mar 29, 2017 at 04:33:20PM +0200, Rafael Knuth wrote:
> I am trying to wrap my head around the super constructor. Simple example:
> class A:
>     def __init__(self):
>         print("world")
> class B(A):
>     def __init__(self):
>         print("hello")
>         super(B, self).__init__()

If you are using Python 2, this cannot work. You cannot use super() 
inside "classic classes" in Python 2, it will fail with TypeError. To 
make this work in Python 2, the class A needs to inherit from object:

    class A(object): ...

So I think you are using Python 3. In Python 3 super() is much easier to 
use, as there is a special shortcut:

    # like super(B, self).__init__()

> B()
> Then I changed the parent class A like this, as I wanted to test how
> the code would look like if I passed arguments:
> class A:
>     def __init__(self, message):
>         self.message = message
>         print(message)
> I then modified the child class B like this:
> class B(A):
>     def __init__(self, message):
>         print("This is the message from your parent class A:")
>         super(B, self).__init__(message)
> B("BlaBla")
> That works, however I am not sure about what exactly happens inside the code.
> What I am concerned about is whether the argument is being actually
> inherited from the parent class A or does B overwrite the argument.

*Arguments* are not inherited. You are confusing two distinct, and 
different, parts of programming.

Inheritance is that if you call a method on a child class, say 
B.method(), but it doesn't exist, it will call the parent class instead, 
say A.method().

When you pass an argument to a method or function, you pass whatever you 
pass, and the method or function receives what you pass. In your 
example, you call B("BlaBla"), so B's __init__ method receives the same 
argument "BlaBla" as the argument "message". Inside B, it then passes on 
message unchanged:

    B.__init__ receives message="BlaBla"
    B.__init__ calls super().__init__(message)
    A.__init__ receives message="BlaBla"

If you want to change the message, of course you can do so:

class C(A):
    def __init__(self, message):
        print("This is the message from your parent class A:")
        super().__init__(message.upper() + "!!!")


And now we have:

    C.__init__ receives message="BlaBla"
    C.__init__ calls super().__init__(modified message)
    A.__init__ receives message="BLABLA!!!"

That is exactly the same process as if we had this instead:

def a_print(message):

def b_print(message):
    print("This is output from a_print:")

def c_print(message):
    print("This is output from a_print:")
    a_print(message.upper() + "!!!")

> Can anyone advise what the correct solution would be (in case mine is wrong).
> Thank you.

You have the right idea. When you call your superclass method:

    super().__init__()  # or any other method

you need to decide what the *purpose of your overridden method is*. If 
B.__init__() does exactly the same as A.__init__(), there is no point in 
writing B.__init__ at all! Just leave it out, and Python will 
automatically do the right thing.

So when you write B.__init__(), you have to decide what you want:

- ignore the superclass method, and just provide your own method:

    class B(A):
        def __init__(self, message):
            self.message = message

(but if you do this for **everything**, then why are you inheriting from 
A? just write B as an independent class).

- call the superclass method, then do post-processing:

    class B(A):
        def __init__(self, message):
            self.message += "?"

- pre-process the argument, then call the superclass method:

    class B(A):
        def __init__(self, message):
            if message == "hello?":
                print("There's nobody here right now.")
                message = "hello"

- or any combination of the above.


More information about the Tutor mailing list