Chain-Calling Parent Initialisers in Python

Chain-calling parent initialisers in python

The way you are doing it is indeed the recommended one (for Python 2.x).

The issue of whether the class is passed explicitly to super is a matter of style rather than functionality. Passing the class to super fits in with Python's philosophy of "explicit is better than implicit".

Calling parent class __init__ with multiple inheritance, what's the right way?

Both ways work fine. The approach using super() leads to greater flexibility for subclasses.

In the direct call approach, C.__init__ can call both A.__init__ and B.__init__.

When using super(), the classes need to be designed for cooperative multiple inheritance where C calls super, which invokes A's code which will also call super which invokes B's code. See http://rhettinger.wordpress.com/2011/05/26/super-considered-super for more detail on what can be done with super.

[Response question as later edited]

So it seems that unless I know/control the init's of the classes I
inherit from (A and B) I cannot make a safe choice for the class I'm
writing (C).

The referenced article shows how to handle this situation by adding a wrapper class around A and B. There is a worked-out example in the section titled "How to Incorporate a Non-cooperative Class".

One might wish that multiple inheritance were easier, letting you effortlessly compose Car and Airplane classes to get a FlyingCar, but the reality is that separately designed components often need adapters or wrappers before fitting together as seamlessly as we would like :-)

One other thought: if you're unhappy with composing functionality using multiple inheritance, you can use composition for complete control over which methods get called on which occasions.

Calling a parent class constructor from a child class in python

Python recommends using super().

Python 2:

super(Instructor, self).__init__(name, year)

Python 3:

super().__init__(name, year)

How to correctly call base class methods (and constructor) from inherited classes in Python?

That is correct. Note that you can also call the __init__ method directly on the Base class, like so:

class Child(Base):
def __init__(self, something_else):
Base.__init__(self, value = 20)
self.something_else = something_else

That's the way I generally do it. But it's discouraged, because it doesn't behave very well in the presence of multiple inheritance. Of course, multiple inheritance has all sorts of odd effects of its own, and so I avoid it like the plague.

In general, if the classes you're inheriting from use super, you need to as well.

Python Constructor Chaining and Polymorphism

You have a __init__() for Man as well as Woman , so that overrides the __init__() from the parent class Human . If you want the __init__() for child class to call the parent's __init__() , then you need to call it using super(). Example -

class Man(Human):
def __init__(self):
super(Man, self).__init__()
print("Hi this is Man Constructor")

class Woman(Human):
def __init__(self):
super(Woman, self).__init__()
print("Hi this is Woman Constructor")

For Python 3.x , you can simply call the parent's __init__() using - super().__init__() .

How to invoke the super constructor in Python?

In line with the other answers, there are multiple ways to call super class methods (including the constructor), however in Python 3 the process has been simplified:

Python 3

class A(object):
def __init__(self):
print("world")

class B(A):
def __init__(self):
print("hello")
super().__init__()

Python 2

In Python 2, you have to call the slightly more verbose version super(<containing classname>, self), which is equivalent to super()as per the docs.

class A(object):
def __init__(self):
print "world"

class B(A):
def __init__(self):
print "hello"
super(B, self).__init__()

how to make child class call parent class __init__ automatically?

I suggest a non-code fix:

Document that super().__init__() should be called by your subclasses before they use any other methods defined in it.

This is not an uncommon restriction. See, for instance, the documentation for threading.Thread in the standard library, which says:

If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread.

There are probably many other examples, I just happened to have that doc page open.

Reason for calling super class constructor using superclass.__init()__ instead of superclass()

As I understand from the book, if Sub inherits Super then one need not call superclass' (Super's) __init__() method.

This is misleading. It's true that you aren't required to call the superclass's __init__ method—but if you don't, whatever it does in __init__ never happens. And for normal classes, all of that needs to be done. It is occasionally useful, usually when a class wasn't designed to be inherited from, like this:

class Rot13Reader:
def __init__(self, filename):
self.file = open(filename):
def close(self):
self.file.close()
def dostuff(self):
line = next(file)
return codecs.encode(line, 'rot13')

Imagine that you want all the behavior of this class, but with a string rather than a file. The only way to do that is to skip the open:

class LocalRot13Reader(Rot13Reader):
def __init__(self, s):
# don't call super().__init__, because we don't have a filename to open
# instead, set up self.file with something else
self.file = io.StringIO(s)

Here, we wanted to avoid the self.file assignment in the superclass. In your case—as with almost all classes you're ever going to write—you don't want to avoid the self.name assignment in the superclass. That's why, even though Python allows you to not call the superclass's __init__, you almost always call it.

Notice that there's nothing special about __init__ here. For example, we can override dostuff to call the base class's version and then do extra stuff:

def dostuff(self):
result = super().dostuff()
return result.upper()

… or we can override close and intentionally not call the base class:

def close(self):
# do nothing, including no super, because we borrowed our file

The only difference is that good reasons to avoid calling the base class tend to be much more common in normal methods than in __init__.


Question: Why do I need to call Super's __init__ using Super.__init__(self, name) OR super(Sub, self).__init__(name) instead of a direct call Super(name)?

Because these do very different things.

Super(name) constructs a new Super instance, calls __init__(name) on it, and returns it to you. And you then ignore that value.

In particular, Super.__init__ does get called one time either way—but the self it gets called with is that new Super instance, that you're just going to throw away, in the Super(name) case, while it's your own self in the super(Sub, self).__init__(name) case.

So, in the first case, it sets the name attribute on some other object that gets thrown away, and nobody ever sets it on your object, which is why self.name later raises an AttributeError.

It might help you understand this if you add something to both class's __init__ methods to show which instance is involved:

class Super:
def __init__(self,name):
print(f"Inside Super __init__ for {self}")
self.name=name
print("Name is:",name)

class Sub(Super):
def __init__(self,name):
print(f"Inside Sub __init__ for {self}")
# line you want to experiment with goes here.

If that last line is super().__init__(name), super(Sub, self).__init__name), or Super.__init__(self, name), you will see something like this:

Inside Sub __init__ for <__main__.Sub object at 0x10f7a9e80>
Inside Super __init__ for <__main__.Sub object at 0x10f7a9e80>

Notice that it's the same object, the Sub at address 0x10f7a9e80, in both cases.

… but if that last line is Super(name):

Inside Sub __init__ for <__main__.Sub object at 0x10f7a9ea0>
Inside Super __init__ for <__main__.Super object at 0x10f7a9ec0>

Now we have two different objects, at different addresses 0x10f7a9ea0 and 0x10f7a9ec0, and with different types.


If you're curious about what the magic all looks like under the covers, Super(name) does something like this (oversimplifying a bit and skipping over some steps1):

_newobj = Super.__new__(Super)
if isinstance(_newobj, Super):
Super.__init__(_newobj, name)

… while super(Sub, self).__init__(name) does something like this:

_basecls = magically_find_next_class_in_mro(Sub)
_basecls.__init__(self, name)

As a side note, if a book is telling you to use super(Sub, self).__init__(name) or Super.__init__(self, name), it's probably an obsolete book written for Python 2.

In Python 3, you just do this:

  • super().__init__(name): Calls the correct next superclass by method resolution order. You almost always want this.
  • super(Sub, self).__init__(name): Calls the correct next superclass—unless you make a mistake and get Sub wrong there. You only need this if you're writing dual-version code that has to run in 2.7 as well as 3.x.
  • Super.__init__(self, name): Calls Super, whether it's the correct next superclass or not. You only need this if the method resolution order is wrong and you have to work around it.2

If you want to understand more, it's all in the docs, but it can be a bit daunting:

  • __new__
  • __init__
  • super (also see Raymond Hettinger's blog post)
  • method invocation (also see the HOWTO)

The original introduction to super, __new__, and all the related features was very helpful to me in understanding all of this. I'm not sure if it'll be as helpful to someone who's not coming at this already understanding old-style Python classes, but it's pretty well written, and Guido (obviously) knows what he's talking about, so it might be worth reading.


1. The biggest cheat in this explanation is that super actually returns a proxy object that acts like _baseclass bound to self in the same way methods are bound, which can be used to bind methods, like __init__. This is useful/interesting knowledge if you know how methods work, but probably just extra confusion if you don't.

2. … or if you're working with old-style classes, which don't support super (or proper method-resolution order). This never comes up in Python 3, which doesn't have old-style classes. But, unfortunately, you will see it in lots of tkinter examples, because the best tutorial is still Effbot's, which was written for Python 2.3, when Tkinter was all old-style classes, and has never been updated.

What are the requirements to cause an initialization of the parent class in child class?

class parent:
def __init__(self, config):
self._config = config

class child(parent):

def __init__(self,config):
parent.__init__(self,config)

def next(self):
print self._config

x = child("test")
x.next() # prints test

All the parameters to be passed to parent must be passed to child to initialize the parent inside the __init__ of child

initialization order in python class hierarchies

What is the exact order of initialization here?

Well, very obviously the one you can see in your code - Base.__init__() is only called when you explicitely ask for it (with the super() call). If Base also has parents and everyone in the chain uses super() calls, the parents initializers will be invoked according to the mro.

Basically, Python is a "runtime language" - except for the bytecode compilation phase, everything happens at runtime - so there's very few "black magic" going on (and much of it is actually documented and fully exposed for those who want to look under the hood or do some metaprogramming).

Does one needs to call super as a first instruction in the init method or is it ok to do it the way I did?

You call the parent's method where you see fit for the concrete use case - you just have to beware of not using instance attributes (directly or - less obvious to spot - indirectly via a method call that depends on those attributes) before they are defined.

I am confused whether it is considered good practice to use super with or without arguments in python > 3.6

If you don't need backward compatibily, use super() without params - unless you want to explicitely skip some class in the MRO, but then chances are there's something debatable with your design (but well - sometimes we can't afford to rewrite a whole code base just to avoid one very special corner case, so that's ok too as long as you understand what you're doing and why).

Now with your core question:

class Derived(Base):
def __init__(self, a, b):
def _process(c, d):
do_something with a and b

self.__class__.__call__ = _process
super(Derived, self).__init__(self)

self.__class__.__call__ is a class attribute and is shared by all instances of the class. This means that you either have to make sure you are only ever using one single instance of the class (which doesn't seem to be the goal here) or are ready to have totally random results, since each new instance will overwrite self.__class__.__call__ with it's own version.

If what you want is to have each instance's __call__ method to call it's own version of process(), then there's a much simpler solution - just make _process an instance attribute and call it from __call__ :

class Derived(Base):
def __init__(self, a, b):
def _process(c, d):
do_something with a and b

self._process = _process
super(Derived, self).__init__(self)

def __call__(self, c, d):
return self._process(c, d)

Or even simpler:

class Derived(Base):
def __init__(self, a, b):
super(Derived, self).__init__(self)
self._a = a
self._b = b

def __call__(self, c, d):
do_something_with(self._a, self._b)

EDIT:

Base requires a callable in ins init method.

This would be better if your example snippet was closer to your real use case.

But when I call super().init() the call method of Derived should not have been instantiated yet or has it?

Now that's a good question... Actually, Python methods are not what you think they are. What you define in a class statement's body using the def statement are still plain functions, as you can see by yourself:

class Foo:
... def bar(self): pass
...
Foo.bar

"Methods" are only instanciated when an attribute lookup resolves to a class attribute that happens to be a function:

Foo().bar
main.Foo object at 0x7f3cef4de908>>
Foo().bar
main.Foo object at 0x7f3cef4de940>>

(if you wonder how this happens, it's documented here)

and they actually are just thin wrappers around a function, instance and class (or function and class for classmethods), which delegate the call to the underlying function, injecting the instance (or class) as first argument. In CS terms, a Python method is the partial application of a function to an instance (or class).

Now as I mentionned upper, Python is a runtime language, and both def and class are executable statements. So by the time you define your Derived class, the class statement creating the Base class object has already been executed (else Base wouldn't exist at all), with all the class statement block being executed first (to define the functions and other class attributes).

So "when you call super().__init()__", the __call__ function of Base HAS been instanciated (assuming it's defined in the class statement for Base of course, but that's by far the most common case).



Related Topics



Leave a reply



Submit