Call derived class method in the init of a base class [duplicate] - python

class A:
def __init__(self):
print("world")
class B(A):
def __init__(self):
print("hello")
B() # output: hello
In all other languages I've worked with the super constructor is invoked implicitly. How does one invoke it in Python? I would expect super(self) but this doesn't work.

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__()

super() returns a parent-like object in new-style classes:
class A(object):
def __init__(self):
print("world")
class B(A):
def __init__(self):
print("hello")
super(B, self).__init__()
B()

With Python 2.x old-style classes it would be this:
class A:
def __init__(self):
print "world"
class B(A):
def __init__(self):
print "hello"
A.__init__(self)

One way is to call A's constructor and pass self as an argument, like so:
class B(A):
def __init__(self):
A.__init__(self)
print "hello"
The advantage of this style is that it's very clear. It call A's initialiser. The downside is that it doesn't handle diamond-shaped inheritance very well, since you may end up calling the shared base class's initialiser twice.
Another way is to use super(), as others have shown. For single-inheritance, it does basically the same thing as letting you call the parent's initialiser.
However, super() is quite a bit more complicated under-the-hood and can sometimes be counter-intuitive in multiple inheritance situations. On the plus side, super() can be used to handle diamond-shaped inheritance. If you want to know the nitty-gritty of what super() does, the best explanation I've found for how super() works is here (though I'm not necessarily endorsing that article's opinions).

Short Answer
super(DerivedClass, self).__init__()
Long Answer
What does super() do?
It takes specified class name, finds its base classes (Python allows multiple inheritance) and looks for the method (__init__ in this case) in each of them from left to right. As soon as it finds method available, it will call it and end the search.
How do I call init of all base classes?
Above works if you have only one base class. But Python does allow multiple inheritance and you might want to make sure all base classes are initialized properly. To do that, you should have each base class call init:
class Base1:
def __init__(self):
super(Base1, self).__init__()
class Base2:
def __init__(self):
super(Base2, self).__init__()
class Derived(Base1, Base2):
def __init__(self):
super(Derived, self).__init__()
What if I forget to call init for super?
The constructor (__new__) gets invoked in a chain (like in C++ and Java). Once the instance is created, only that instance's initialiser (__init__) is called, without any implicit chain to its superclass.

Just to add an example with parameters:
class B(A):
def __init__(self, x, y, z):
A.__init__(self, x, y)
Given a derived class B that requires the variables x, y, z to be defined, and a superclass A that requires x, y to be defined, you can call the static method init of the superclass A with a reference to the current subclass instance (self) and then the list of expected arguments.

I use the following formula that extends previous answers:
class A(object):
def __init__(self):
print "world"
class B(A):
def __init__(self):
print "hello"
super(self.__class__, self).__init__()
B()
This way you don't have to repeat the name of the class in the call to super. It can come handy if you are coding a large number of classes, and want to make your code in the initialiser methods independent of the class name.

Related

Init functions execution in multiple inheritance in python

I am new to python and trying to understand the inheritance in python. Python has a feature of multiple inheritance. A single class can inherit more than one class at a same time. When we create an object of child class,the init function of child class is called. I want to call the init function of both the parent class of the child, but i am able to call only only one init function.I read the concept of method resolution order, by which the left most class inherited init function will be called. Please correct my code, so that the init function of both parent classes is called.
class A:
def __init__(self):
print("in A Init")
class B:
def __init__(self):
print("in B Init")
class C(B,A):
def __init__(self):
super().__init__()
print("in C Init")
cObj= C()
All the __init__ functions need to call super().__init__(), like this:
class A:
def __init__(self):
super().__init__()
print("in A Init")
class B:
def __init__(self):
super().__init__()
print("in B Init")
class C(B, A):
def __init__(self):
super().__init__()
print("in C Init")
c_obj= C()
When you invoke this you get the following output:
in A Init
in B Init
in C Init
Per the super() function documentation, it returns a reference to "a parent or sibling" of the class, whichever is next in the method resolution order. At the top of the hierarchy, it returns a reference to the implicit parent class object, which has an empty __init__ method which does nothing.
In order for this to work well, it's best for all the inherited __init__ functions to have the same signature, including the common base class; in this case, the signature is just __init__(self) (no additional arguments), and the common base class is object, which also has __init__(self) with no additional arguments, so that's all good. Another common pattern is for them all to take keyword arguments and pass through **kwargs to the next one.

How can I make a method variable available to inherited subclass?

Trying to invoke a method variable of superclass in the subclass.Is this possible, If possible, is it a good practice or design?
I have written an example code to execute the flow. Below is the code
class bar_for_foo_mixin():
def __init__(self):
self.z =0
def bar(self, q):
x=2
y=8
bar_for_foo_mixin.z= x+y+q
print(bar_for_foo_mixin.z)
def extented_bar(self):
self.bar(10)
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
print("hello",self.z)
class oldFoo():
def __init__(self):
self.var = bar_for_foo_mixin()
self.var.bar(10)
Now how can I make the Z value and make it useful in the subclass? But If Call method extended_bar() in the subclass the above code works fine, But is there any way to store the method variable of superclass and use it in the subclass?
A third class call bar method.
Expected output to print. The Z vaue should store the computations done in the superclass method bar.
hello 20
You can make the __init__ method of the subclass call the base class' __init__ method via the super() function:
class myfoo(bar_for_foo_mixin):
def __init__(self):
super().__init__()
print("hello",self.z)
Moreover, since z is initialized as an instance variable, your bar function should update it as a variable bound to self instead of the class:
def bar(self, q):
x=2
y=8
self.z= x+y+q
print(self.z)

What is the difference between having parameters within super and the init parentheses .... super().__init__()

I understand that the concept of super().__init__() is to do with inheritance and I have seen code with parameters within init. However, I came across a code sample that had this:
class Maze(tk.Tk, object):
def __init__(self):
super(Maze, self).__init__()
the parameters are now within the super parenthesis. What is the difference and why may one be used over the other? Thank you
This is the original way super() was intended to work:
super(Maze, self).__init__()
This is also the only way that it did work in Python 2.
So, why the arguments?
You want to call __init__() of the class which is the super class if Maze (probably tk.Tk), bound to self. To do that, you have to pass the arguments Maze and self to super, so that it knows what to do.
What does it actually do?
super(Maze, self).__init__ has to determine type(self) to extract the MRO from it, i.e. the order in which classes are inherited from one another.
Then, in that list, it finds the class which is just above Maze and looks for an __init__ in that class or any class above it. When it finds it, it bounds the __init__ init method to self (i.e. fixes its first argument, so you don't have to pass it).
You could implement that version of super yourself. It would be something like this:
class my_super:
def __init__(self, cls, obj):
self.cls = cls
self.obj = obj
def __getattribute__(self, method_name):
cls = object.__getattribute__(self, 'cls')
obj = object.__getattribute__(self, 'obj')
mro = type(obj).__mro__
mro_above_cls = mro[mro.index(cls)+1:]
for super_cls in mro_above_cls:
if hasattr(super_cls, method_name):
method = getattr(super_cls, method_name)
return functools.partial(method, self)
Note that you don't have to call this from a method at all. You could do this:
class A:
def f(self):
print('A')
class B(A):
def f(self):
print('B')
a = A()
b = B()
super(B, b).f() # prints: A
my_super(B, b).f() # prints: A
What about the version without arguments?
super(Maze, self).__init__() was very explicit, but almost all of Python code always used current class and self as arguments, so Python 3 made it easier by providing a magic super() which knows what you want.

How to invoke the super constructor in Python?

class A:
def __init__(self):
print("world")
class B(A):
def __init__(self):
print("hello")
B() # output: hello
In all other languages I've worked with the super constructor is invoked implicitly. How does one invoke it in Python? I would expect super(self) but this doesn't work.
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__()
super() returns a parent-like object in new-style classes:
class A(object):
def __init__(self):
print("world")
class B(A):
def __init__(self):
print("hello")
super(B, self).__init__()
B()
With Python 2.x old-style classes it would be this:
class A:
def __init__(self):
print "world"
class B(A):
def __init__(self):
print "hello"
A.__init__(self)
One way is to call A's constructor and pass self as an argument, like so:
class B(A):
def __init__(self):
A.__init__(self)
print "hello"
The advantage of this style is that it's very clear. It call A's initialiser. The downside is that it doesn't handle diamond-shaped inheritance very well, since you may end up calling the shared base class's initialiser twice.
Another way is to use super(), as others have shown. For single-inheritance, it does basically the same thing as letting you call the parent's initialiser.
However, super() is quite a bit more complicated under-the-hood and can sometimes be counter-intuitive in multiple inheritance situations. On the plus side, super() can be used to handle diamond-shaped inheritance. If you want to know the nitty-gritty of what super() does, the best explanation I've found for how super() works is here (though I'm not necessarily endorsing that article's opinions).
Short Answer
super(DerivedClass, self).__init__()
Long Answer
What does super() do?
It takes specified class name, finds its base classes (Python allows multiple inheritance) and looks for the method (__init__ in this case) in each of them from left to right. As soon as it finds method available, it will call it and end the search.
How do I call init of all base classes?
Above works if you have only one base class. But Python does allow multiple inheritance and you might want to make sure all base classes are initialized properly. To do that, you should have each base class call init:
class Base1:
def __init__(self):
super(Base1, self).__init__()
class Base2:
def __init__(self):
super(Base2, self).__init__()
class Derived(Base1, Base2):
def __init__(self):
super(Derived, self).__init__()
What if I forget to call init for super?
The constructor (__new__) gets invoked in a chain (like in C++ and Java). Once the instance is created, only that instance's initialiser (__init__) is called, without any implicit chain to its superclass.
Just to add an example with parameters:
class B(A):
def __init__(self, x, y, z):
A.__init__(self, x, y)
Given a derived class B that requires the variables x, y, z to be defined, and a superclass A that requires x, y to be defined, you can call the static method init of the superclass A with a reference to the current subclass instance (self) and then the list of expected arguments.
I use the following formula that extends previous answers:
class A(object):
def __init__(self):
print "world"
class B(A):
def __init__(self):
print "hello"
super(self.__class__, self).__init__()
B()
This way you don't have to repeat the name of the class in the call to super. It can come handy if you are coding a large number of classes, and want to make your code in the initialiser methods independent of the class name.

Polymorphism - adding to existing methods while overwriting them

I want to be able to subclass a class, and define __init__ but still run the old __init__ as well.
To illustrate, say I have the following classes:
class A(object):
def __init__(self):
self.var1 = 1
class B(A):
def __init__(self)
self.var2 = 2
doInitForA()
And I want to be able to do this:
instB = B()
print (instB.var1) #1
print (instB.var2) #2
Edited as Ignacio Vazquez-Abrams suggested. (Is it possible to edit without bumping?)
replace
doInitForA()
with
super(b, self).__init__()
You might want to look at this question: Chain-calling parent constructors in python, specifically use the super(b, self).__init__() method.
Either call a.__init__(self) or derive a from object and use super().
class a:
def __init__(self):
self.var1 = 1
class b(a):
def __init__(self)
self.var2 = 2
a.__init__(self)
You can even write super().__init__() if you are using python 3.
See this question about the use of super().
Call your father's c'tor from within your c'tor: a.__init__(self). Note that you need to pass self as first parameter. If the parent c'tor takes more parameters, pass them after self.

Categories

Resources