pydata

Keep Looking, Don't Settle

python class (6): python 2 and python 3 class definition with super()

1. Why need super()

In a single inheritance case (when you subclass one class only), your new class inherits methods of the base class. This includes __init__. So if you don't define it in your class, you will get the one from the base.

Things start being complicated if you introduce multiple inheritance (subclassing more than one class at a time). This is because if more than one base class has __init__, your class will inherit the first one only.

In such cases, you should really use super if you can, I'll explain why. But not always you can. The problem is that all your base classes must also use it (and their base classes as well -- the whole tree).

If that is the case, then this will also work correctly (in Python 3 but you could rework it into Python 2 -- it also has super):

## python3
class A:
    def __init__(self):
        print('A')
        super().__init__()

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

class C(A, B):
    pass

C()
#prints:
#A
#B
## python2
class A(object):
    def __init__(self):
        print('A')
        super(A, self).__init__()

class B(object):
    def __init__(self):
        print('B')
        super(B, self).__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

Notice how both base classes use super even though they don't have their own base classes.

What super does is: it calls the method from the next class in MRO (method resolution order). The MRO for C is: (C, A, B, object). You can print C.__mro__ to see it.

So, C inherits __init__ from A and super in A.__init__ calls B.__init__ (B follows A in MRO).

So by doing nothing in C, you end up calling both, which is what you want.

Now if you were not using super, you would end up inheriting A.__init__ (as before) but this time there's nothing that would call B.__init__ for you.

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

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()  # output print: A

To fix that you have to define C.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

The problem with that is that in more complicated MI trees, __init__ methods of some classes may end up being called more than once whereas super/MRO guarantee that they're called just once.

2. How to use super()

super() (without arguments) was introduced in Python 3: super() -> same as super(__class__, self)

Python 2 equivalent for new-style classes: super(CurrentClass, self)

for old-style classes you can always use:

class Classname(OldStyleParent):
    def __init__(self, *args, **kwargs):
        OldStyleParent.__init__(self, *args, **kwargs)

Reference

  1. super() fails with error: TypeError “argument 1 must be type, not classobj”
  2. Python inheritance: TypeError: object.init() takes no parameters
  3. Python extending with - using super() python 3 vs python 2
  4. Understanding Python super() with init() methods