Calling `super()` in parent class - python

I'm reading Raymond Hettinger's Python’s super() considered super! About a page in, there's this example:
class Shape:
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
cs = ColoredShape(color='red', shapename='circle')
Why is it necessary to call super() in Shape here? My understanding is that this calls object.__init__(**kwds) since Shape implicitly inherits from object.
Even without that statement, we've already
established shapename already in the parent's __init__,
established the child class's color in an explicit method override,
then invoked the parent's __init__ with super() in ColoredShape.
As far as I can tell, dropping this line produces the same behavior & functionality:
class Shape: # (object)
def __init__(self, shapename, **kwds):
self.shapename = shapename
# super().__init__(**kwds)
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
def check(self):
print(self.color)
print(self.shapename)
cs = ColoredShape(color='red', shapename='circle')
cs.check()
# red
# circle
What is the purpose of super() within Shape here?

The point is cooperative multiple inheritance. The point of the whole entire article is cooperative multiple inheritance, really.
You look at Shape and you don't see any parents besides object. Sure, but that doesn't mean there aren't any siblings, or anything else on the MRO after Shape. super() isn't just for superclasses; it searches for the next implementation of the method in the method resolution order. For example, one of the later classes in the article is
class MovableColoredShape(ColoredShape, MoveableAdapter):
pass
In this case, Shape.__init__ needs to call super().__init__, or MoveableAdapter.__init__ and all further __init__ calls will be skipped.

I see that #user2357112 has already provided a correct answer. I was working on an example that I'd though I'd leave here because it's pretty much what user2357112 is describing. Consider a mixin class like this:
class PositionMixin:
def __init__(self, x=0, y=0, **kwds):
super().__init__(**kwds)
self.x = x
self.y = y
Let's say you apply that to your ColoredShape class:
class ColoredShape(Shape, PositionMixin):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
If Shape doesn't call super.__init__, then when you do this:
myshape = ColoredShape('red', shapename='circle', x=1, y=1)
print(myshape.x, myshape.y)
You get:
Traceback (most recent call last):
File "supertest.py", line 18, in <module>
print (myshape.x, myshape.y)
AttributeError: 'ColoredShape' object has no attribute 'x'
The call to super.__init__ in shape is necessary to call the __init__ method on PositionMixin.

Related

How to: safely call super constructors with different arguments

I have seen super().__init__(*args) used to call the super constructor safely (in a way that does not fail to diamond inheritence). However I cannot find a way to call different super constructors with different arguments in this way.
Here is an example illustraiting the problem.
from typing import TypeVar, Generic
X = TypeVar("X")
Y = TypeVar("Y")
class Base:
def __init__(self):
pass
class Left(Base, Generic[X]):
def __init__(self, x:X):
super().__init__()
self.lft = x
class TopRight(Base, Generic[Y]):
def __init__(self, y:Y):
super().__init__()
self.rgh = y
class BottomRight(TopRight[Y], Generic[Y]):
def __init__(self, y:Y):
super().__init__(y + y)
class Root(Left[X], BottomRight[Y], Generic[X, Y]):
def __init__(self, x:X, y:Y):
pass #issue here
#does not work
#super().__init__(x)
#super().__init__(y)
#calls base twice
#Left[X].__init__(x)
#BottomRight[Y].__init__(y)
How do I call Left.__init__(x) and BottomRight.__init__(y) seperately and safely?
The thing is that to be use in cooperative form, the intermediate classes have to accept the arguments that are not "aimed" at them, and pass those on on their own super call, in a way that becomes transparent.
You them do not place multiple calls to your ancestor classes: you let the language runtime do that for you.
Your code should be written:
from typing import Generic, TypeVar
X = TypeVar("X")
Y = TypeVar("Y")
class Base:
def __init__(self):
pass
class Left(Base, Generic[X]):
def __init__(self, x:X, **kwargs):
super().__init__(**kwargs)
self.lft = x
class TopRight(Base, Generic[Y]):
def __init__(self, y:Y, **kwargs):
super().__init__(**kwargs)
self.rgh = y
class BottomRight(TopRight[Y], Generic[Y]):
def __init__(self, y:Y, **kwargs): # <- when this is executed, "y" is extracted from kwargs
super().__init__(y=y + y, **kwargs) # <- "x" remains in kwargs, but this class does not have to care about it.
class Root(Left[X], BottomRight[Y], Generic[X, Y]):
def __init__(self, x:X, y:Y):
super().__init__(x=x, y=y) # <- will traverse all superclasses, "Generic" being last
Also, note that depending on your project's ends, and final complexity, these type annotations may gain you nothing, and instead, add complexity to a code otherwise trivial. They are not always a gain in Python projects, although due to circunstances the tooling (i.e. IDEs), might recommend them.
Also, check this similar answer from a few days ago, were I detail a bit more of Python method resolution order mechanisms, and point to the official documentation on them: In multiple inheritance in Python, init of parent class A and B is done at the same time?

Are you allowed to use a method inside the init method?

I just started learning about OOP in python3 and we made this little class today.
class Square:
def __init__(self, side):
self.side = side
def show_perimeter(self):
print(self.side * 4)
test_square = Square(10)
test_square.show_perimeter()
>> 40
Now I'm thinking if it's possible to get the value of the perimeter while creating the object, something like...
class Square:
def __init__(self, side):
self.side = side
self.permiter = get_perimeter(self)
def get_perimeter(self):
return self.side * 4
test_square = Square(10)
print(test_square.perimeter)
Is this something you're able to do?
If so, is it a good practice?
If not, what would be the best way to get the perimeter by just using the side?
There is nothing special about the __init__ method other than it is invoked automatically as part of the construction of an object (__new__ creates the object, and __init__ initializes it). Therefore, you can do whatever you need to do within it. However, you need to make sure that you don't inadvertently do things that would cause operations on a partially initialized object. The use of #property below can solve most of these edge cases.
The only difference here is it is better form to call self.method() rather than method(self) in most cases.
class Square:
def __init__(self, side):
self.side = side
self.permiter = self.get_perimeter()
def get_perimeter(self):
return self.side * 4
test_square = Square(10)
print(test_square.perimeter)
However, I'd like to point out that a property might be better in this case:
class Square():
def __init__(self, side):
self.side = side;
#property
def perimeter(self):
return self.side * 4
x = Square(10)
print(x.perimeter)
>>> 40
In this case, the #property decorator converts the perimeter method to a property which can be accessed just like it is another attribute, but it is calculated at the time it is asked for.
It's allowed, but it's also a lot more dangerous than people usually realize.
Say you have the following class:
class Thing:
def __init__(self):
self._cached_size = self.compute_size()
def compute_size(self):
return 1000
That seems to work fine on its own, but then if you try to subclass it:
class SubThing(Thing):
def __init__(self, more_stuff):
super().__init__()
self.more_stuff = more_stuff
def compute_size(self):
return super().compute_size() + self.more_stuff
SubThing(5)
Everything goes to pieces, because Thing.__init__ calls SubThing.compute_size, and SubThing.compute_size assumes self.more_stuff is ready, but it's not ready:
Traceback (most recent call last):
File "./prog.py", line 14, in <module>
File "./prog.py", line 9, in __init__
File "./prog.py", line 3, in __init__
File "./prog.py", line 12, in compute_size
AttributeError: 'SubThing' object has no attribute 'more_stuff'
You should essentially never call methods in __init__ that you expect to be overridden, since your object is in a very precarious, half-constructed state, especially the parts of its state that subclasses are responsible for.
If you want to use your own class's version of a method and ignore overrides (dodging the problem of subclass state), you can call it directly:
class Thing:
def __init__(self):
self._cached_size = Thing.compute_size(self)
def compute_size(self):
return 1000
Some people might recommend having subclasses initialize their state before calling super().__init__, but that leads into a really nastily coupled mess of different classes depending on specific parts of other classes being ready. It's not going to decrease the number of problems you have.
You should reference the method in the same class with self.
class Square:
def __init__(self, side):
self.side = side
self.perimeter = self.get_perimeter()
def get_perimeter(self):
return self.side * 4

Python Multiple Inheritance: Argument passing (**kwargs) and super()

I am trying to understand Python multiple inheritance and I kind of understand MRO, super() and passing arguments in MI, but while I was reading the below example it kind of confused me.
class Contact:
all_contacts = []
def __init__(self, name=None, email=None, **kwargs):
super().__init__(**kwargs)
self.name = name
self.email = email
self.all_contacts.append(self)
class AddressHolder:
def __init__(self, street=None, city=None, state=None, code=None, **kwargs):
super().__init__(**kwargs)
self.street = street
self.city = city
self.state = state
self.code = code
class Friend(Contact, AddressHolder):
def __init__(self, phone='', **kwargs):
super().__init__(**kwargs)
self.phone = phone
Now what I fail to understand is why use super() in Contact and AddressHolder class. I mean super() is used when we are inheriting from a parent class but both Contact & AddressHolder are not inheriting from any other class. (technically they are inheriting from object). This example confuses me with the right use of super()
All (new style) classes have a linearized method resolution order (MRO). Depending on the inheritance tree, actually figuring out the MRO can be a bit mind-bending, but it is deterministic via a relatively simple algorithm. You can also check the MRO through a class's __mro__ attribute.
super gets a delegator to the next class in the MRO. In your example, Friend has the following MRO:
Friend -> Contact -> AddressHolder -> object
If you call super in one of Friend's methods, you'll get a delegator that delegates to Contact's methods. If that method doesn't call super, you'll never call the methods on AddressHolder. In other words, super is responsible for calling only the next method in the MRO, not ALL the remaining methods in the MRO.
(If you call super in one of Friend's methods and Contact doesn't have its own implementation of that method, then super will delegate to AddressHolder, or whichever class has the next implementation for that method in the MRO.)
This is all well and good since object has a completely functional __init__ method (so long as **kwargs is empty at that point). Unfortunately, it doesn't work if you are trying to resolve the call chain of some custom method. e.g. foo. In that case, you want to insert a base class that all of the base classes inherit from. Since that class is a base for all of the classes (or at least base classes) to inherit from. That class will end up at the end of the MRO and can do parameter validation1:
class FooProvider:
def foo(self, **kwargs):
assert not kwargs # Make sure all kwargs have been stripped
class Bar(FooProvider):
def foo(self, x, **kwargs):
self.x = x
super().foo(**kwargs)
class Baz(FooProvider):
def foo(self, y, **kwargs):
self.y = y
super().foo(**kwargs)
class Qux(Bar, Baz):
def foo(self, z, **kwargs):
self.z = z
super().foo(**kwargs)
demo:
>>> q = Qux()
>>> q.foo(x=1, y=2, z=3)
>>> vars(q)
{'z': 3, 'y': 2, 'x': 1}
>>> q.foo(die='invalid')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'z'
>>>
>>> q.foo(x=1, y=2, z=3, die='invalid')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/google/home/mgilson/sandbox/super_.py", line 18, in foo
super().foo(**kwargs)
File "/usr/local/google/home/mgilson/sandbox/super_.py", line 8, in foo
super().foo(**kwargs)
File "/usr/local/google/home/mgilson/sandbox/super_.py", line 13, in foo
super().foo(**kwargs)
File "/usr/local/google/home/mgilson/sandbox/super_.py", line 3, in foo
assert not kwargs # Make sure all kwargs have been stripped
AssertionError
Note, you can still have default arguments with this approach so you don't lose too much there.
1Note, this isn't the only strategy to deal with this problem -- There are other approaches you can take when making mixins, etc. but this is by far the most robust approach.

Partially initializing base class

Couldn't think of a better question title. Feel free to edit. I have a base class which is inherited by many classes (which in turn may have more sub-classes). For each class, I have a sequence of operations I need to perform post-initialization. The sequence is encapsulated in a function runme() which performs a series of object method calls
class myBase(object):
def __init__(self,neg,op,value):
self.neg = neg
self.op = op
self.value = value
#Process
self.runme()
def runme(self):
self.preprocess()
self.evaluate()
self.postprocess()
def preprocess(self):
pass
def evaluate(self):
pass
def postprocess(self):
pass
The sub-classes have to accept the same attributes as base (and any additional attributes). All of them will over-ride the three functions - preprocess, evaluate and postprocess
class childA(myBase):
def __init__(self,neg,op,value,ad1):
super(childA,self).__init__(neg,op,value)
self.ad1 = ad1
#Must call runme() here again??
runme()
def evaluate():
#Something using self.ad1
blah = self.ad1+self.value
The way I see it, it creates a problem - childA calls the base __init__ first, which calls runme(), which in turn will call evaluate. Since child over-rides evaluate, the child's definition of evaluate is executed, but as self.ad1 has not yet been instantiated, this throws an AttributeError
I can remove self.runme() from myBase and the problem will probably be gone, but I can further sublcass childA into childAA
class childAA(childA):
def __init__(self,neg,op,value,ad1):
super(childAA,self).__init__(neg,op,value,ad1)
self.runme()
And the problem can manifest all over again. I can't remove runme() from childA's __init__ because objects of both childA and childAA can be formed (and will need processing)
Currently, as a workaround, I do not call runme() in the __init__, instead call it from the calling program after initialization.
obja=childA(foo,bar,baz,ad1)
obja.runme()
A simpler alternative is to call super() at the end of child's __init__, but that does not appear to be right
Another way is - Tell the base class to defer calling of runme() to the child class. Is this possible? Say in myBase, I do
def __init__(self,neg,op,value):
self.neg = neg
self.op = op
self.value = value
#Process
if some_condition which checks if this is being called by a derived class:
self.runme()
Which if these is the best way to solve it? Alternatively, is this a common problem and what are other suggested solutions?
EDIT
Two answers were posted (and deleted) which concurred the best way seems to be to leave runme() call in the base-class and then call the super() at the end of child's __init__
class myBase(object):
def __init__(self,neg,op,value):
self.neg = neg
self.op = op
self.value = value
#Process
self.runme()
class childA(myBase):
def __init__(self,neg,op,value,ad1):
self.ad1 = ad1
super(childA,self).__init__(neg,op,value)
In the case where you need values that depend on existing values,
class childA(myBase):
def __init__(self,neg,op,value,ad1):
self.ad1 = ad1
self.internal_value = self.value #Not yet initialized!!
super(childA,self).__init__(neg,op,value)
this code can be put in the preprocess() or some other function that gets called first in runme()
def preprocess(self):
self.internal_value = value
#Rest of the stuff
If the children's __init__ require partially initialized objects to proceed, calling super() at the end will not work indeed. If that's the case, you could call runme form __new__ in myBase:
class myBase(object):
def __new__(cls, *args, **kwargs):
obj = super(myBase, cls).__new__(cls)
obj.__init__(*args, **kwargs)
obj.runme()
def __init__(self, a):
print 'myBase init'
self.list = ['myBase', a]
def runme(self):
print 'myBase:', self.list
class ChildA(myBase):
def __init__(self, a, b):
print 'ChildA init'
super(ChildA, self).__init__(a)
self.list.extend(['ChildA', b])
def runme(self):
print 'ChildA:', self.list
class ChildAA(ChildA):
def __init__(self, a, b, c):
print 'ChildAA init'
super(ChildAA, self).__init__(a, b)
self.list.extend(['ChildAA', c])
def runme(self):
print 'ChildAA:', self.list
You can order the code inside the various __init__ functions as required by the initialisation process, and the proper runme function will always be called after __init__ completes:
>>> ChildA(1, 2)
ChildA init
myBase init
ChildA: ['myBase', 1, 'ChildA', 2]
>>> ChildAA(1, 2, 3)
ChildAA init
ChildA init
myBase init
ChildAA: ['myBase', 1, 'ChildA', 2, 'ChildAA', 3]

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

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)
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)
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.
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/
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
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())

Categories