[Tutor] difference between super() and parent.__init__()?

Alex Hall mehgcap at gmail.com
Sat Oct 22 02:51:36 CEST 2011


On 10/21/11, Steven D'Aprano <steve at pearwood.info> wrote:
> Alex Hall wrote:
>> Hi all,
>> I am just curious: I have seen classes that are subclasses initialize
>> their parents through both super and parentClass.__init__. What is the
>> difference, if any, and is one "better" or "more pythonic" than the
>> other?
>
>
> A simple question with a complicated answer...
>
>
> First off, the answer depends on whether you are using so-called
> "classic classes" or "new-style classes".
I always use new-style (python2.7).
>
> If you are using Python 3, there are only new-style classes: classic
> classes are gone. But in Python 2, classic classes are written like this:
>
> class MyClass:  # Don't inherit from anything.
>      pass
>
>
> while new-style classes inherit from either object or some other
> built-in type which inherits from object:
>
> class MyClass(object):  # Inherit from object, new-style class.
>      pass
> class MyInt(int): pass
> class MyStr(str): pass
> class MyList(list): pass
> # etc.
>
>
> super *does not work* for classic classes, end of story. So in the
> following discussion, I'm only talking about new-style classes, the only
> sort of class in Python 3.
>
> Secondly, let's talk about the difference between simple, single
> inheritance and multiple inheritance. Single inheritance is when each
> class inherits from exactly one parent, or superclass:
>
> class A(object): pass
> class B(A): pass
> class C(B): pass
>
> gives an inheritance hierarchy (a family tree, if you like):
>
> object
>    |
>    A
>    |
>    B
>    |
>    C
>
>
> In the case of single inheritance, super doesn't do anything special. It
> is the recommended way to handle inheritance: you are encouraged to
> write something like this:
>
> class C(B):
>      def method(self, arg):
>          x = super().method(arg)  # Only works in Python 3
>          # or in Python 2, use super(C, self).method(arg)
>
> instead of x = B.method(arg), but functionality-wise, there is no
> difference between the two. In Python 3, super becomes a little more
> convenient, but it doesn't really matter much which you use. (But keep
> reading, for a reason why you *should* use super even in single
> inheritance.)
>
> The situation is more complicated once you have multiple inheritance. In
> multiple inheritance, at least one of the classes involved inherits from
> two or more superclasses. Here's a particularly simple example:
>
> class A(object): pass
> class B(A): pass
> class C(A): pass
> class D(B, C): pass  # multiple superclasses
>
> or:
>
> object
>    |
>    A
>   / \
> B   C
>   \ /
>    D
>
>
> In full generality, multiple inheritance can be so complex that most
> programming languages prohibit it, or put severe restrictions on it.
> Python is one of the very few that support it with very few restrictions.
>
> The difficulty occurs when you override a method in D. Now you need to
> ensure that each superclass (B and C) gets a shot at having their method
> called too. Here is a simple example showing how NOT to do it:
>
> class A(object):
>      def save(self):
>          print("class A saves")
>
> class B(A):
>      def save(self):
>          print("B saves stuff")
>          A.save(self)  # call the parent class method too
>
> class C(A):
>      def save(self):
>          print("C saves stuff")
>          A.save(self)
>
> class D(B, C):
>      def save(self):
>          print ("D saves stuff")
>          # make sure you let both B and C save too
>          B.save(self)
>          C.save(self)
>
>
> Simple, obvious... but wrong. Try it and see:
>
>  >>> d = D()
>  >>> d.save()
> D saves stuff
> B saves stuff
> class A saves
> C saves stuff
> class A saves
>
> The problem is that the A.save method gets called twice.
>
> In general, it is very hard to solve this problem by hand, especially if
> you have a complex family tree. You have to manage the entire family
> tree, keeping track of which superclasses you have already called, and
> avoid calling them a second time. That's fiddly and annoying and hard to
> do right.
>
> But super can do it for you. Here's a version that works correctly:
>
> class A(object):
>      def save(self):
>          print("class A saves")
>
> class B(A):
>      def save(self):
>          print("B saves stuff")
>          super(B, self).save()
>          # In Python 3, you can abbreviate this as super().save()
>
> class C(A):
>      def save(self):
>          print("C saves stuff")
>          super(C, self).save()
>
> class D(B, C):
>      def save(self):
>          print ("D saves stuff")
>          super(D, self).save()
>
>
> and in use:
>
>  >>> d = D()
>  >>> d.save()
> D saves stuff
> B saves stuff
> C saves stuff
> class A saves
>
> Now each class gets called exactly once, and in the right order.
That all makes sense, and explains a lot.
>
> All the smarts managing the entire inheritance hierarchy is built into
> super, so each method gets called once and exactly once, provided that
> every class *only* uses super. If you try to mix super calls and direct
> method calls like B.method(self, arg), it probably won't work correctly,
> and if it does, it will probably be by accident.
I'll keep it in mind; super() is to be *always* used, or never at all.
>
> So please use super, even in single inheritance. Otherwise you are
> restricting the usefulness of your class: it can never be used with
> multiple inheritance.
>
> The one exception to this is if your class changes the method signature.
> E.g. if A.method takes no arguments, but B.method requires an argument.
> super cannot help you now. But changing the signature of methods is
> almost always the wrong thing to do: it is a violation of good object
> oriented design, and should be avoided. The main time it is justified is
> in the class constructor methods, __new__ or __init__, in which case you
> may need to avoid super and *carefully* manage the inheritance by hand.
I'm not sure about this part. Could I not simply do something like:
class a(object):
 def m(self, p1): pass

class b(a):
 def m(self, p1, p2, *args, **kwords):
  super(b, self).m(*args, **kwords) #different signatures, but a.m()
still gets what it wants, if called correctly

Also, what is the difference between __init__ (what I always use) and
__new__? If you can change these, why not other methods? Of course
these would be the most commonly changed, but the question stands.
Thanks!
>
> --
> Steven
>
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> http://mail.python.org/mailman/listinfo/tutor
>


-- 
Have a great day,
Alex (msg sent from GMail website)
mehgcap at gmail.com; http://www.facebook.com/mehgcap


More information about the Tutor mailing list