Polymorphism - adding to existing methods while overwriting them - python

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.

Related

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

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.

Inheritance: why does self refer to child class?

Sorry I could not come up with a better title.
Please consider this example:
class A:
def __init__(self, x):
self.attr = x
class B(A):
x = 1
def __init__(self):
super().__init__(x=self.x)
class C(B):
x = 2
def __init__(self):
super().__init__()
c = C()
print(c.attr) # 2
This code prints 2.
This means self in B.__init__ is an instance of C, not B.
I thought that the purpose of super was to refer to the methods/attributes of the parent class. Can somebody please explain the logic behind the output not being 1? Thank you.
The purpose of super() in super().__init__() is to call the __init__() method from the parent. It doesn't change the object itself -- it's still an instance of the subclass. So anything that the subclass overrides will be visible in the instance.
If the parent method doesn't want the overridden value of x, it should use B.x rather than self.x. The whole point of accessing the attribute through self is to take advantage of overriding in subclasses.

Calling __init__ of all parent class with different parameters [duplicate]

This question already has answers here:
python multiple inheritance passing arguments to constructors using super
(4 answers)
Closed 3 years ago.
I have some class structure like this:
class A:
def __init__(self):
print("A")
class B:
def __init__(self, x):
print("B")
class C(A,B):
def __init__(self):
super(C, self).__init__() # ???
c=C()
As you can see, A and B both are parent class of C. I want to call both classes __init__ in C's __init__. I want to pass some parameter to B's __init__. Is there any way I can do this with super keyword? I think that this will work:
class C(A,B):
def __init__(self):
A.__init__()
B.__init__(5)
Should we call __init__ directly in such a way, or is there any better solution for this?
You have to provide self as parameter to parent classes' __init__().
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self, 5)

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.

Categories

Resources