Python Multiple Inheritance child(parent1, parent2) access parent2 __init__ - python

let's say I have this class
class child(parent1, parent2):
pass
Would it be possible to access parent2.__init__, if parent1 also has a defined __init__ ?.
Here is my complete code.
class parent1:
def __init__(self):
self.color="Blue"
class parent2:
def __init__(self):
self.figure="Triangle"
class child(parent1,parent2):
pass
juan=child()
try:
print(juan.color)
except Exception as e:
print(e)
try:
print(juan.figure)
except Exception as e:
print(e)
print(juan.__dict__)
I've tried with
class child(parent1,parent2):
def __init__(self):
super(parent2).__init__()
but maybe I am missing something?
Thanks.
Regards.

parent1 and parent2, if expected to be used in a cooperative multiple inheritance setting, should call super.
class parent1:
def __init__(self):
super().__init__()
self.color = "Blue"
class parent2:
def __init__(self):
super().__init__()
self.figure = "Triangle"
When you define child, its method resolution order determines which __init__ gets called first, as well as determining which class super() refers to when each time it gets called. In your actual example,
class child(parent1,parent2):
pass
parent1.__init__ is called first (since child does not override __init__), and its use of super() refers to parent2. If you had instead defined
class child2(parent2, parent1):
pass
then parent2.__init__ would be called first, and its use of super() would refer to parent1.
super() is used not to ensure that object.__init__ (which doesn't do anything) is called, but rather object.__init__ exists so that it can be called once a chain of super() calls reaches the end. (object.__init__ itself does not use super, as it is guaranteed to be the last class in the method resolution order of any other class.)

Related

can i use super() to refer to two different parent classes [duplicate]

I have the following two superclasses:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
I would like to have a child class that inherits from both be able to call super for both parents.
class Child(Parent1, Parent2):
def on_start(self):
# super call on both parents
What is the Pythonic way to do this? Thanks.
Exec summary:
Super only executes one method based on the class hierarchy's __mro__. If you want to execute more than one method by the same name, your parent classes need to written to cooperatively do that (by calling super implicitly or explicitly) or you need to loop over __bases__ or the __mro__ values of the child classes.
The job of super is to delegate part or all of a method call to some existing method in the classes ancestor tree. The delegation may go well outside of classes that you control. The method name delegated needs to exist in the group of base classes.
The method presented below using __bases__ with try/except is closest to a complete answer to your question of how to call each parent's method of the same name.
super is useful in the situation where you want to call one of your parent's methods, but you don't know which parent:
class Parent1(object):
pass
class Parent2(object):
# if Parent 2 had on_start - it would be called instead
# because Parent 2 is left of Parent 3 in definition of Child class
pass
class Parent3(object):
def on_start(self):
print('the ONLY class that has on_start')
class Child(Parent1, Parent2, Parent3):
def on_start(self):
super(Child, self).on_start()
In this case, Child has three immediate parents. Only one, Parent3, has an on_start method. Calling super resolves that only Parent3 has on_start and that is the method that is called.
If Child inherits from more than one class that has an on_start method, the order is resolved left to right (as listed in the class definition) and bottom to top (as logical inheritance). Only one of the methods is called and the other methods of the same name in the hierarchy of classes have been superseded.
So, more commonly:
class GreatGrandParent(object):
pass
class GrandParent(GreatGrandParent):
def on_start(self):
print('the ONLY class that has on_start')
class Parent(GrandParent):
# if Parent had on_start, it would be used instead
pass
class Child(Parent):
def on_start(self):
super(Child, self).on_start()
If you want to call multiple parents methods by method name, you can use __bases__ instead of super in this case and iterate over the base classes of Child without knowing the classes by name:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
base.on_start(self)
>>> Child().on_start()
do something
do something else
If there is a possibility one of the base classes does not have on_start you can use try/except:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Parent3(object):
pass
class Child(Parent1, Parent2, Parent3):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
do something
do something else
"Parent3" does not have an "on_start"
Using __bases__ will act similar to super but for each class hierarchy defined in the Child definition. ie, it will go though each forbearer class until on_start is satisfied once for each parent of the class:
class GGP1(object):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
def on_start(self):
print('GP1 do something else')
class Parent1(GP1):
pass
class GGP2(object):
def on_start(self):
print('GGP2 do something')
class GP2(GGP2):
pass
class Parent2(GP2):
pass
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
GP1 do something else
GGP2 do something
# Note that 'GGP1 do something' is NOT printed since on_start was satisfied by
# a descendant class L to R, bottom to top
Now imagine a more complex inheritance structure:
If you want each and every forbearer's on_start method, you could use __mro__ and filter out the classes that do not have on_start as part of their __dict__ for that class. Otherwise, you will potentially get a forbearer's on_start method. In other words, hassattr(c, 'on_start') is True for every class that Child is a descendant from (except object in this case) since Ghengis has an on_start attribute and all classes are descendant classes from Ghengis.
** Warning -- Demo Only **
class Ghengis(object):
def on_start(self):
print('Khan -- father to all')
class GGP1(Ghengis):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
pass
class Parent1(GP1):
pass
class GGP2(Ghengis):
pass
class GP2(GGP2):
pass
class Parent2(GP2):
def on_start(self):
print('Parent2 do something')
class Child(Parent1, Parent2):
def on_start(self):
for c in Child.__mro__[1:]:
if 'on_start' in c.__dict__.keys():
c.on_start(self)
>>> Child().on_start()
GGP1 do something
Parent2 do something
Khan -- father to all
But this also has a problem -- if Child is further subclassed, then the child of Child will also loop over the same __mro__ chain.
As stated by Raymond Hettinger:
super() is in the business of delegating method calls to some class in
the instance’s ancestor tree. For reorderable method calls to work,
the classes need to be designed cooperatively. This presents three
easily solved practical issues:
1) the method being called by super() needs to exist
2) the caller and callee need to have a matching argument signature and
3) every occurrence of the method needs to use super()
The solution is to write cooperative classes that uniformly use super through the ancestor list or creative use of the adapter pattern to adapt classes you cannot control. These methods are discussed more completely in the article Python’s super() considered super! by Raymond Hettinger.
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Child(Parent1, Parent2):
def on_start(self):
super(Child, self).on_start()
super(Parent1, self).on_start()
c = Child()
c.on_start()
do something
do something else
Or without super:
class Child(Parent1, Parent2):
def on_start(self):
Parent1.on_start(self)
Parent2.on_start(self)
In your case, since both of the parents implements the same method, super will just be the same as the first parent inherited, from left to right (for your code, Parent1). Calling two functions with super is impossible. To do what you want, you must simply call the method from the parent class, as follows:
class Child(Parent1, Parent2):
def on_start (self):
Parent1.on_start()
Parent2.on_start()
super() calls are meant to be cooperative.
One standard idiom for this goes as follows:
Everything ultimately inherits from a base that can discard unnecessary keyword arguments (to avoid an exception from forwarding them to object.__init__).
Every class calls super(), including the base.
Every __init__ accepts **kwargs; initializes local stuff according to specific keywords; then passes the information along the super chain.
The calling code will pass keyword args necessary for initializing every class in the chain.
So, for example:
class Person:
def __init__(self, **kwargs):
print(f'Initializing a Person')
pass
class Parent1(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom1 = kwargs.get('heirloom1', None)
print(f'Set up Parent1 of the {self.__class__} with {self.heirloom1!r}')
class Parent2(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom2 = kwargs.get('heirloom2', None)
print(f'Set up Parent2 of the {self.__class__} with {self.heirloom2!r}')
class Child(Parent1, Parent2):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.inheritance = kwargs.get('inheritance', None)
spoiled_rotten = Child(inheritance='mansion', heirloom1='trust fund', heirloom2='sports car')
Note in particular that, when spoiled_rotten is created, super().__init__(**kwargs) will forward to Parent2, not Person, and that Person.__init__ is only called once. super doesn't mean "the base class"; it means "the next class in the MRO of self" - and self is a Child instance throughout the entire process.

PyLint W0231 super-init-not-called with __init__ of grandparent

I make a Child class that use methods from Parent class witch not present in GrandParent. But in the Child.__init__() class I don't want to get some effects from Parent.__init__() and instead I call call GrandParent.__init__()
class GrandParent():
...
class Parent(GrandParent):
def __init__(self):
GrandParent.__init__(self)
...
class Child(Parent):
def __init__(self):
GrandParent.__init__(self)
...
With this code i got next warnings from pylint
W0233: __init__ method from a non direct base class 'GrandParent' is called (non-parent-init-called)
W0231: __init__ method from base class 'Parent' is not called (super-init-not-called)
What is the correct way to fix this warnings ?
(or disable this warnings for Child is proper solution ?)
class Child(Parent):
def __init__(self): # pylint: disable=W0233
GrandParent.__init__() # pylint: disable=W0231
...
You can try multiple ways such as:
GrandParent.__init__(self)
super().super().__init__(self)
super(GrandParent, self).__init__()
but the error will still be there either as:
super-init-not-called
or bad-super-call (super(GrandParent, self)...)
or even as useless super call to the immediate parent.
I suppose it's meant as a warning for not initializing the immediate class in the class __mro__ by mistake if you work with inheritance and you can to turn off it with a pylint disable:
# pylint: disable=super-init-not-called
but you are messing with the __mro__ chain by such calls.
What you can do is allow cascading through the Child.__mro__ items upward i.e. Child -> Parent -> GrandParent (-> object) while skipping by class reference in an __init__:
class Parent(GrandParent):
def __init__(self):
super().__init__()
if self.__class__ == Child:
return
which does not trigger the PyLint warnings and allows the correct behavior explicitly and without skipping any required items in __mro__.
IMO it's much cleaner preserving the __mro__ which seems what PyLint's intent is as well by super-init-not-called warning. On the other hand, it's kind of tricky and hides the code in a completely different class and that will make the debugging in the future annoying if some properties from Parent will be missing and no comment/disable warning are present in Child class.
If you want to pull the methods out of the GrandParent class only, simply go with the "mixins" classes which is what Django REST Framework is using and don't write any __init__ for those (or if, then don't forget super().__init__() to continue in __mro__ chain!).
class GrandParent:
def __init__(self):
print("> GrandParent __init__()")
class Mixin:
def mymethod(self):
print("Hey")
class Parent(Mixin, GrandParent):
def __init__(self):
print("> Parent __init__()")
super().__init__()
class Child(Mixin, GrandParent):
def __init__(self):
print("> Child __init__()")
super().__init__()
Child().mymethod()
# > Child __init__()
# > GrandParent __init__()
# Hey
Parent().mymethod()
# > Parent __init__()
# > GrandParent __init__()
# Hey
which will basically be the same situation you have with Child -> Parent -> GrandParent but Mixin itself doesn't have (any useful) __init__ which will cause your code only from GrandParent to execute due to Mixin.__init__() being empty hence doing visually the same thing as with if self.__class__ == Child: return but without any hacks + with increased readability.

How can I add to the initial definition of a python class inheriting from another class?

I'm trying to define self.data inside a class inheriting from a class
class Object():
def __init__(self):
self.data="1234"
class New_Object(Object):
# Code changing self.data here
But I ran into an issue.
class Object():
def __init__(self):
self.data="1234"
So I have the beginning class here, which is imported from elsewhere, and let's say that the class is a universal one so I can't modify the original at all.
In the original, the instance is referred to as "self" inside the class, and it is defined as self inside the definition __init__.
class New_Object(Object):
# Code changing self.data here
So if I wanted to inherit from the class Object, but define self.data inside New_Object, I thought I would have to define __init__ in New_Object, but this overrides the __init__ from New_Object
Is there any way I could do this without copypasting the __init__ from Object?
You use super to call the original implementation.
class New_Object(Object):
def __init__(self):
super(NewObject, self).__init__()
self.info = 'whatever'
That's what super is for:
class NewObject(Object):
def __init__(self):
super(NewObject, self).__init__()
# self.data exists now, and you can modify it if necessary
You can use super().__init__() to call Object.__init__() from New_Object.__init__().
What you would do:
class Object:
def __init__(self):
print("Object init")
self.data = "1234"
class New_Object(Object):
def __init__(self):
print("calling super")
super().__init__()
print("data is now", self.data)
self.data = self.data.split("3")
o = New_Object()
# calling super
# Object init
# data is now 1234
Note that you do not have to give any arguments to super(), as long as you are using Python 3.
The answer is that you call the superclass's __init__ explicitly during the subclass's __init__. This can be done either of two ways:
Object.__init__(self) # requires you to name the superclass explicitly
or
super(NewObject, self).__init__() # requires you to name the subclass explicitly
The latter also requires you to ensure that you're using "new-style" classes: in Python 3 that's always the case, but in Python 2 you must be sure to inherit from the builtin object class. In Python 3 it can actually be expressed even more simply:
super().__init__()
Personally, in most of my code the "disadvantage" of having to name the superclass explicitly is no disadvantage at all, and Object.__init__() lends transparency since it makes it absolutely clear what is being called. This is because most of my code is single-inheritance only. The super route comes into its own when you have multiple inheritance. See What does 'super' do in Python?
Python 2 example:
class Object(object):
def __init__(self):
self.data = "1234"
class NewObject:
def __init__(self):
# subclass-specific stuff
super(NewObject, self).__init__()
# more subclass-specific stuff

Python Multiple Inheritance: call super on all

I have the following two superclasses:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
I would like to have a child class that inherits from both be able to call super for both parents.
class Child(Parent1, Parent2):
def on_start(self):
# super call on both parents
What is the Pythonic way to do this? Thanks.
Exec summary:
Super only executes one method based on the class hierarchy's __mro__. If you want to execute more than one method by the same name, your parent classes need to written to cooperatively do that (by calling super implicitly or explicitly) or you need to loop over __bases__ or the __mro__ values of the child classes.
The job of super is to delegate part or all of a method call to some existing method in the classes ancestor tree. The delegation may go well outside of classes that you control. The method name delegated needs to exist in the group of base classes.
The method presented below using __bases__ with try/except is closest to a complete answer to your question of how to call each parent's method of the same name.
super is useful in the situation where you want to call one of your parent's methods, but you don't know which parent:
class Parent1(object):
pass
class Parent2(object):
# if Parent 2 had on_start - it would be called instead
# because Parent 2 is left of Parent 3 in definition of Child class
pass
class Parent3(object):
def on_start(self):
print('the ONLY class that has on_start')
class Child(Parent1, Parent2, Parent3):
def on_start(self):
super(Child, self).on_start()
In this case, Child has three immediate parents. Only one, Parent3, has an on_start method. Calling super resolves that only Parent3 has on_start and that is the method that is called.
If Child inherits from more than one class that has an on_start method, the order is resolved left to right (as listed in the class definition) and bottom to top (as logical inheritance). Only one of the methods is called and the other methods of the same name in the hierarchy of classes have been superseded.
So, more commonly:
class GreatGrandParent(object):
pass
class GrandParent(GreatGrandParent):
def on_start(self):
print('the ONLY class that has on_start')
class Parent(GrandParent):
# if Parent had on_start, it would be used instead
pass
class Child(Parent):
def on_start(self):
super(Child, self).on_start()
If you want to call multiple parents methods by method name, you can use __bases__ instead of super in this case and iterate over the base classes of Child without knowing the classes by name:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
base.on_start(self)
>>> Child().on_start()
do something
do something else
If there is a possibility one of the base classes does not have on_start you can use try/except:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Parent3(object):
pass
class Child(Parent1, Parent2, Parent3):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
do something
do something else
"Parent3" does not have an "on_start"
Using __bases__ will act similar to super but for each class hierarchy defined in the Child definition. ie, it will go though each forbearer class until on_start is satisfied once for each parent of the class:
class GGP1(object):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
def on_start(self):
print('GP1 do something else')
class Parent1(GP1):
pass
class GGP2(object):
def on_start(self):
print('GGP2 do something')
class GP2(GGP2):
pass
class Parent2(GP2):
pass
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
GP1 do something else
GGP2 do something
# Note that 'GGP1 do something' is NOT printed since on_start was satisfied by
# a descendant class L to R, bottom to top
Now imagine a more complex inheritance structure:
If you want each and every forbearer's on_start method, you could use __mro__ and filter out the classes that do not have on_start as part of their __dict__ for that class. Otherwise, you will potentially get a forbearer's on_start method. In other words, hassattr(c, 'on_start') is True for every class that Child is a descendant from (except object in this case) since Ghengis has an on_start attribute and all classes are descendant classes from Ghengis.
** Warning -- Demo Only **
class Ghengis(object):
def on_start(self):
print('Khan -- father to all')
class GGP1(Ghengis):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
pass
class Parent1(GP1):
pass
class GGP2(Ghengis):
pass
class GP2(GGP2):
pass
class Parent2(GP2):
def on_start(self):
print('Parent2 do something')
class Child(Parent1, Parent2):
def on_start(self):
for c in Child.__mro__[1:]:
if 'on_start' in c.__dict__.keys():
c.on_start(self)
>>> Child().on_start()
GGP1 do something
Parent2 do something
Khan -- father to all
But this also has a problem -- if Child is further subclassed, then the child of Child will also loop over the same __mro__ chain.
As stated by Raymond Hettinger:
super() is in the business of delegating method calls to some class in
the instance’s ancestor tree. For reorderable method calls to work,
the classes need to be designed cooperatively. This presents three
easily solved practical issues:
1) the method being called by super() needs to exist
2) the caller and callee need to have a matching argument signature and
3) every occurrence of the method needs to use super()
The solution is to write cooperative classes that uniformly use super through the ancestor list or creative use of the adapter pattern to adapt classes you cannot control. These methods are discussed more completely in the article Python’s super() considered super! by Raymond Hettinger.
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Child(Parent1, Parent2):
def on_start(self):
super(Child, self).on_start()
super(Parent1, self).on_start()
c = Child()
c.on_start()
do something
do something else
Or without super:
class Child(Parent1, Parent2):
def on_start(self):
Parent1.on_start(self)
Parent2.on_start(self)
In your case, since both of the parents implements the same method, super will just be the same as the first parent inherited, from left to right (for your code, Parent1). Calling two functions with super is impossible. To do what you want, you must simply call the method from the parent class, as follows:
class Child(Parent1, Parent2):
def on_start (self):
Parent1.on_start()
Parent2.on_start()
super() calls are meant to be cooperative.
One standard idiom for this goes as follows:
Everything ultimately inherits from a base that can discard unnecessary keyword arguments (to avoid an exception from forwarding them to object.__init__).
Every class calls super(), including the base.
Every __init__ accepts **kwargs; initializes local stuff according to specific keywords; then passes the information along the super chain.
The calling code will pass keyword args necessary for initializing every class in the chain.
So, for example:
class Person:
def __init__(self, **kwargs):
print(f'Initializing a Person')
pass
class Parent1(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom1 = kwargs.get('heirloom1', None)
print(f'Set up Parent1 of the {self.__class__} with {self.heirloom1!r}')
class Parent2(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom2 = kwargs.get('heirloom2', None)
print(f'Set up Parent2 of the {self.__class__} with {self.heirloom2!r}')
class Child(Parent1, Parent2):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.inheritance = kwargs.get('inheritance', None)
spoiled_rotten = Child(inheritance='mansion', heirloom1='trust fund', heirloom2='sports car')
Note in particular that, when spoiled_rotten is created, super().__init__(**kwargs) will forward to Parent2, not Person, and that Person.__init__ is only called once. super doesn't mean "the base class"; it means "the next class in the MRO of self" - and self is a Child instance throughout the entire process.

How do I call a Child class method from within a Parent class Method?

I know this question might be pointless but there is a reason why I am looking to do it this way. I want to call something exactly opposite to super()
class A(object):
def use_attack(self, damage, passive, spells):
#do stuff with passed parameters
#return something
def use_spell(self, name , enemy_hp):
#other code
if name == 'Enrage':
#call child method use_attack right here
class B(A):
def use_attack(self):
#bunch of code here
return super(B, self).use_attack(damage, passive, spells)
def use_spell(self, name , enemy_hp):
return super(B , self).use_attack(name ,enemy_hp)
b = B()
b.use_spell('Enrage', 100)
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
I would like to call the child method use_attack() in the line indicated.
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
Then factor that code out into a method on the parent class. This is exactly what inheritance is for. Children inherit code from parents, not the other way around.
From the python docs: "The mro attribute of the type lists the method resolution search order used by both getattr() and super()"
https://docs.python.org/3/library/functions.html#super
This should help shed some light on Inheritance and Method Resolution Order (mro).
class Foo(object):
def __init__(self):
print('Foo init called')
def call_child_method(self):
self.child_method()
class Bar(Foo):
def __init__(self):
print('Bar init called')
super().__init__()
def child_method(self):
print('Child method called')
bar = Bar()
bar.call_child_method()

Categories

Resources