Python extending with - using super() Python 3 vs Python 2

162,369

Solution 1

  • super() (without arguments) was introduced in Python 3 (along with __class__):

    super() -> same as super(__class__, self)
    

    so that would be the 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)
    

Solution 2

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):

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

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()
#prints:
#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.

Solution 3

In short, they are equivalent. Let's have a history view:

(1) at first, the function looks like this.

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) to make code more abstract (and more portable). A common method to get Super-Class is invented like:

    super(<class>, <instance>)

And init function can be:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

However requiring an explicit passing of both the class and instance break the DRY (Don't Repeat Yourself) rule a bit.

(3) in V3. It is more smart,

    super()

is enough in most case. You can refer to http://www.python.org/dev/peps/pep-3135/

Solution 4

Just to have a simple and complete example for Python 3, which most people seem to be using now.

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

gives

42
chickenman

Solution 5

Another python3 implementation that involves the use of Abstract classes with super(). You should remember that

super().__init__(name, 10)

has the same effect as

Person.__init__(self, name, 10)

Remember there's a hidden 'self' in super(), So the same object passes on to the superclass init method and the attributes are added to the object that called it. Hence super()gets translated to Person and then if you include the hidden self, you get the above code frag.

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())
Share:
162,369
oz123
Author by

oz123

I am a Software Engineer usually working as DevOps or Back-End developer. I feel comfortable using various Linux distributions (usually at work I have Debian\Ubuntu\Red Hat\CentOS and even {Open,}SuSE. Whenever I can, I use Gentoo and NetBSD). My tools of choice are Python, Go and C. I wrote a little book about Kubernetes Operators in Python and Go! Take a look at my CLI framework for Python.

Updated on November 25, 2020

Comments

  • oz123
    oz123 over 3 years

    Originally I wanted to ask this question, but then I found it was already thought of before...

    Googling around I found this example of extending configparser. The following works with Python 3:

    $ python3
    Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
    [GCC 4.6.3] on linux2
    >>> from configparser import  SafeConfigParser
    >>> class AmritaConfigParser(SafeConfigParser):
    ...     def __init__(self):
    ...         super().__init__()
    ... 
    >>> cfg = AmritaConfigParser()
    

    But not with Python 2:

    >>> class AmritaConfigParser(SafeConfigParser):
    ...       def __init__(self):
    ...           super(SafeConfigParser).init()
    ... 
    >>> cfg = AmritaConfigParser()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in __init__
    TypeError: must be type, not classob
    

    Then I read a little bit on Python New Class vs. Old Class styles (e.g. here. And now I am wondering, I can do:

    class MyConfigParser(ConfigParser.ConfigParser):
          def Write(self, fp):
              """override the module's original write funcition"""
              ....
          def MyWrite(self, fp):
              """Define new function and inherit all others"""
    

    But, shouldn't I call init? Is this in Python 2 the equivalent:

     class AmritaConfigParser(ConfigParser.SafeConfigParser):
        #def __init__(self):
        #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
        #
        # is this the equivalent of the above ? 
        def __init__(self):
            ConfigParser.SafeConfigParser.__init__(self)
    
  • akaRem
    akaRem about 12 years
    Notice how both base classes use super even though they don't have their own base classes. They have. In py3k every class subclasses object.
  • jpmc26
    jpmc26 almost 9 years
    -1. This answer didn't clarify anything for me. In Python 2, super(__class__) gives NameError: global name '__class__' is not defined, and super(self.__class__) is erroneous as well. You must provide an instance as a second argument, which would suggest you need to do super(self.__class__, self), but that is wrong. If Class2 inherits from Class1 and Class1 calls super(self.__class__, self).__init__(), Class1's __init__ will then call itself when instantiating an instance of Class2.
  • jpmc26
    jpmc26 almost 9 years
    To clarify a point, I get TypeError: super() takes at least 1 argument (0 given) when trying to call super(self.__class__) in Python 2. (Which doesn't make a lot of sense, but it demonstrates how much information is missing from this answer.)
  • mata
    mata almost 9 years
    @jpmc26: in python2 you get this error because your trying to call __init__() without argument on the unbound super object (which you get by calling super(self.__class__) with only one argument), you need a bound super object then it should work: super(CurrentClass, self).__init__(). Don't use self.__class__ because that will always refer to the same class when calling a parent and therfore create an infinite loop if that parent also does the same.
  • CristiFati
    CristiFati almost 6 years
    __class__ (member) also exists in Python2.
  • mata
    mata almost 6 years
    @CristiFati This isn't about the __class__ member but about the implicitly created lexical __class__ closure which always refers to the class currently being defined, which doesn't exist in python2.
  • dturvene
    dturvene over 4 years
    This is the answer I was looking for, but didn't know how to ask. The MRO description is good.