I have multiple parent classes with methods of the same name that are inherited by the child class. I'm wondering if it is possible to specify which parent class the child class uses in the call to super(). Depending on what argument is passed, I would want my child class to use the methods from the specified parent class. Thanks in advance
class ParentOne:
def __init__(self, payload):
self.payload = payload
def foo(self):
message = f"From ParentOne: {self.payload}"
return message
class ParentTwo:
def __init__(self, payload):
self.payload = payload
def foo(self):
message = f"From ParentTwo: {self.payload}"
return message
class Child(ParentOne, ParentTwo):
def __init__(self, payload):
## How to specify which parent to init with??
super().__init__(payload=payload)
You do this very much as you described: pass in the desired parent and use it.
Since you're not using the parent-resolution mechanism, you'll have to directly call the desired __init__ as a class method, and supply the child as an explicit argument:
class Child(ParentOne, ParentTwo):
def __init__(self, payload, parent):
## How to specify which parent to init with??
parent.__init__(self, payload=payload)
child1 = Child("cargo 1", ParentOne)
child2 = Child("cargo 2", ParentTwo)
Output:
init 1 used
init 2 used
Related
As part of a message parser I have something similar to what is below, I want to be able to override some of the base class message functions as needed, but the override only works if I add a pass through function. I have lots of these functions and would like to not have to use a pass through function for every message function the child might override.
I know I could provide the functions that the child wants to override as part of the init append operation from the child, but the real messages are a lot more complicated than this simple example and doing that would break the rules I have set for the child classes.
First question is why does it not just work without the pass though, dictionaries are mutable, so the override should be called from the dictionary.
Second question, is there a solution that does not entail the pass though function or appending the override functions to the dictionary from the child init?
class Base():
def set_enable_ind(self):
print("using the pass through hack")
self.set_enable(self)
def set_enable(self):
print("In base class set_enable")
# base msg config dict
msgConfig = {
'setEnable': (set_enable),
}
msgConfigInd = {
'setEnable': (set_enable_ind),
}
def __init__(self, msgConfig=None):
# add the child's messages to the default ones
if msgConfig:
self.msgConfig.update(msgConfig)
self.msgConfigInd.update(msgConfig)
#this one calls the function directly
def msg_recived(self,msg):
self.msgConfig[msg](self)
# this one calls the pass though function
def msg_recived_ind(self,msg):
self.msgConfigInd[msg](self)
class Child(Base):
# override from base class
def set_enable(self, data = None, header=None):
print("In child class set_enable")
# add my own
def disconnect(self, data = None, header=None):
print("In child class disconnect_connection")
# each entry gets a function to call
BtMsgs = {
'disconnect' : (disconnect),
}
def __init__(self):
# call base class __init__ function first, adding my messages
Base.__init__(self, Child.BtMsgs)
if __name__ == "__main__":
child = Child()
#these both work as expected
child.msg_recived('disconnect')
child.msg_recived_ind('disconnect')
#this will always call the Base class function
child.msg_recived('setEnable')
#this will call the child function
child.msg_recived_ind('setEnable')
#output is as follow:
#In child class disconnect_connection
#In child class disconnect_connection
#In base class set_enable
#using the pass through hack
#In child class set_enable
The reason your msg_recived function always calls the Base.set_enable method is because of scoping. When you do:
class Base:
def set_enable(self):
print("In base class set_enable")
msgConfig = {
'setEnable': set_enable, # I removed parens here
}
...
you are telling Python to reference the nearest-scoped set_enable function (the one right above the dictionary).
The reason your Child class never calls its set_enable method, is because you haven't updated the reference in its own msgConfig dictionary. Calling the self.msgConfig.update method is only adding the new key/val reference to the disconnect method of the Child class.
If you change the Child.BtMsgs attribute/dict to have new references, it will work:
class Child(Base):
...
BtMsgs = {
'disconnect': disconnect,
'setEnable': set_enable,
}
def __init__(self):
super().__init__(Child.BtMsgs) # This is Python3.X+ convention
...
Output:
In child class disconnect_connection
In child class disconnect_connection
In child class set_enable
In child class set_enable
This is because 'setEnable': set_enable is now scoped to the one defined inside the Child class.
Regarding my original comment on your question about using __getitem__, getattr, and setattr:
You don't have to subclass dict for these methods to be useful.
class MsgConfig:
def __init__(self, **kwargs):
for attr in kwargs:
setattr(self, attr, kwargs[attr])
def __getitem__(self, item):
return self.__dict__.get(item)
def update(self, d):
if isinstance(d, MsgConfig):
d = d.__dict__
for k,v in d.items():
setattr(self, k, v)
class Base:
def set_enable(self):
print("In base class set_enable")
def __init__(self, msgConfig=None):
self.msgConfig = MsgConfig(setEnable=self.set_enable)
if msgConfig:
self.msgConfig.update(msgConfig)
def msg_recived(self,msg):
self.msgConfig[msg](self)
class Child(Base):
def set_enable(self, data = None, header=None):
print("In child class set_enable")
def disconnect(self, data = None, header=None):
print("In child class disconnect_connection")
def __init__(self):
super().__init__(MsgConfig(disconnect=self.disconnect))
if __name__ == "__main__":
child = Child()
child.msg_recived('disconnect')
child.msg_recived('setEnable')
Output:
In child class disconnect_connection
In child class set_enable
You could try modifying or extending the above class to work for you to automatically pull in necessary message function pointers.
I would like to overwrite an inherited method in a class (see below example for __init__ method) while letting its children still use the Parents version.
I know that I could achieve the desired behaviour redefining the __init__ method in the GrandChild class or using multiple inheritance. However my question aims at a way to achieve the same with changes only to the Child class and its __init__ implementation.
(The actual use case is significantly more complex 'legacy code' with several classes on each level. The motivation of this question is therefore to achieve the desired behaviour in the respective class without having to touch the implementation of the other classes or the inheritance structure)
If this is impossible I would also appreciate an explanation to that effect.
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
# This throws a Type error right now since it calls the init method of class Child
grandchild = GrandChild("d", "e")
EDIT:
As mentioned above I am aware that I can achieve the desired behaviour in different ways such as changing the class structure (as below). However the question is really more about wether python allows doing it with changes only to the Child class. If this is actually impossible (not merely undesirable) in python, an explanation why would do more to answer my question than providing alternative implementations that change anything beyond the implementation of the Child class.
class ChildCommonFunctionality(Parent):
# Use this class for all common functionality originally provided by Child Class
pass
class Child(ChildCommonFunctionality):
# Use this class to override the init method
def __init__(self, a):
super().__init__(a, None)
class GrandChild(ChildCommonFunctionality):
# This Class should use the __init__ method of class Parent
pass
I have found a way using _init_subclass to make sure that all subclasses of Child use the constructor of Parent instead of the one defined in Child inspired by this post:
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
def __init_subclass__(cls):
cls.__init__ = super().__init__
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
Even though this is a bit hacky it provides the desired functionality of actually bypassing Childs init method
You could do :
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
super().__init__(a, b) # Or None instead of b... but that's not good when called by GrandChild
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
grandchild = GrandChild("d", "e")
EDIT : you could also replace the optional parameter by a mandatory one in GrandChild :
class GrandChild(Child):
def __init__(self, a, b):
super().__init__(a, b)
This code might do the trick, adding a few lines to the suggestion of #dspr:
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
if type(self) == Child:
if b is not None:
raise ValueError(
"Second argument is not allowed for direct use in Child class")
super().__init__(a, None) #Or (a, b) if you trust b to be None as it is here
else:
super().__init__(a, b)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
print(child.b) # None
grandchild = GrandChild("d", "e")
print(grandchild.b) # e
child = Child("f", "g")
print(child.b) # ValueError
Consider the following python code:
class Parent(object):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
class ChildA(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child A")
class ChildB(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child B")
class GrandChild(ChildA, ChildB):
def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
self.a_name = a_name
self.b_name = b_name
self.a_serial_number = a_serial_number
self.b_serial_number = b_serial_number
super(GrandChild, self).__init_( something )
When running the super function in GrandChild, what is the proper way to format the __init__ arguments so that ChildA and ChildB both get the correct arguments?
Also how do you access the two different versions of the speak method (ChildA's version and ChildB's version) from within the GrandChild class?
so, when you call super from the grandchild, ChildA's __init__ method will be called because super follows the __mro__ property (parents left to right then grandparents left-to-right, then great grandparents, ...)
Since ChildA's init also calls super, then all the super calls will be chained, calling child b's __init__ and eventually the parent init.
For that to work, your interface generally needs to be consistent. That is positional arguments need to mean the same things, and be in the order.
In situations where that's not the case, keyword arguments may work better.
class Parent:
def __init__(self, name, serial, **kwargs):
self.name = name
self.serial = serial
class ChildA(Parent):
def __init__(self, a_name, a_serial, **kwargs):
self.a_name = a_name
self.a_serial = a_serial
super().__init__(**kwargs)
class ChildB(Parent):
def __init__(self, b_name, b_serial, **kwargs):
self.b_name = b_name
self.b_serial = b_serial
super().__init__(**kwargs)
class GrandChild(ChildA, ChildB):
def __init__(self):
super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)
Also note that in your code name and serial are reused as instance properties between all the classes and that's probably not what you want.
In python, you can explicitly call a particular method on (one of) your parent class(es):
ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)
Note that you need to put the self in explicitly when calling this way.
You can also – as you did – use the super() way, which will call the "first" parent. The exact order is dynamic, but by default it will do left-to-right, depth-first, pre-order scans of your inheritance hierarchy. Hence, your super() call will only call __init__ on ChildA.
I wrote a Python module, with several classes that inherit from a single class called MasterBlock.
I want to import this module in a script, create several instances of these classes, and then get a list of all the existing instances of all the childrens of this MasterBlock class. I found some solutions with vars()['Blocks.MasterBlock'].__subclasses__() but as the instances I have are child of child of MasterBlock, it doesn't work.
Here is some example code:
Module:
Class MasterBlock:
def main(self):
pass
Class RandomA(MasterBlock):
def __init__(self):
pass
# inherit the main function
Class AnotherRandom(MasterBlock):
def __init__(self):
pass
# inherit the main function
Script:
import module
a=module.RandomA()
b=module.AnotherRandom()
c=module.AnotherRandom()
# here I need to get list_of_instances=[a,b,c]
Th ultimate goal is to be able to do:
for instance in list_of_instances:
instance.main()
If you add a __new__() method as shown below to your base class which keeps track of all instances created in a class variable, you could make the process more-or-less automatic and not have to remember to call something in the __init__() of each subclass.
class MasterBlock(object):
instances = []
def __new__(cls, *args, **kwargs):
instance = super(MasterBlock, cls).__new__(cls, *args, **kwargs)
instance.instances.append(instance)
return instance
def main(self):
print('in main of', self.__class__.__name__) # for testing purposes
class RandomA(MasterBlock):
def __init__(self):
pass
# inherit the main function
class AnotherRandom(RandomA): # works for sub-subclasses, too
def __init__(self):
pass
# inherit the main function
a=RandomA()
b=AnotherRandom()
c=AnotherRandom()
for instance in MasterBlock.instances:
instance.main()
Output:
in main of RandomA
in main of AnotherRandom
in main of AnotherRandom
What about adding a class variable, that contains all the instances of MasterBlock? You can record them with:
Class MasterBlock(object):
all_instances = [] # All instances of MasterBlock
def __init__(self,…):
…
self.all_instances.append(self) # Not added if an exception is raised before
You get all the instances of MasterBlock with MasterBlock.all_instances (or instance.all_instances).
This works if all base classes call the __init__ of the master class (either implicitly through inheritance or explicitly through the usual super() call).
Here's a way of doing that using a class variable:
class MasterBlock(object):
instances = []
def __init__(self):
self.instances.append(self)
def main(self):
print "I am", self
class RandomA(MasterBlock):
def __init__(self):
super(RandomA, self).__init__()
# other init...
class AnotherRandom(MasterBlock):
def __init__(self):
super(AnotherRandom, self).__init__()
# other init...
a = RandomA()
b = AnotherRandom()
c = AnotherRandom()
# here I need to get list_of_instances=[a,b,c]
for instance in MasterBlock.instances:
instance.main()
(you can make it simpler if you don't need __init__ in the subclasses)
output:
I am <__main__.RandomA object at 0x7faa46683610>
I am <__main__.AnotherRandom object at 0x7faa46683650>
I am <__main__.AnotherRandom object at 0x7faa46683690>
Is it possible to leave a parent class unspecified until an instance is created?
e.g. something like this:
class SomeParentClass:
# something
class Child(unspecifiedParentClass):
# something
instance = Child(SomeParentClass)
This obviously does not work. But is it possible to do this somehow?
You can change the class of an instance in the class' __init__() method:
class Child(object):
def __init__(self, baseclass):
self.__class__ = type(self.__class__.__name__,
(baseclass, object),
dict(self.__class__.__dict__))
super(self.__class__, self).__init__()
print 'initializing Child instance'
# continue with Child class' initialization...
class SomeParentClass(object):
def __init__(self):
print 'initializing SomeParentClass instance'
def hello(self):
print 'in SomeParentClass.hello()'
c = Child(SomeParentClass)
c.hello()
Output:
initializing SomeParentClass instance
initializing Child instance
in SomeParentClass.hello()
Have you tried something like this?
class SomeParentClass(object):
# ...
pass
def Child(parent):
class Child(parent):
# ...
pass
return Child()
instance = Child(SomeParentClass)
In Python 2.x, also be sure to include object as the parent class's superclass, to use new-style classes.
You can dynamically change base classes at runtime. Such as:
class SomeParentClass:
# something
class Child():
# something
def change_base_clase(base_class):
return type('Child', (base_class, object), dict(Child.__dict__))()
instance = change_base_clase(SomeParentClass)
For example:
class Base_1:
def hello(self):
print('hello_1')
class Base_2:
def hello(self):
print('hello_2')
class Child:pass
def add_base(base):
return type('Child', (base, object), dict(Child.__dict__))()
# if you want change the Child class, just:
def change_base(base):
global Child
Child = type('Child', (base, object), dict(Child.__dict__))
def main():
c1 = add_base(Base_1)
c2 = add_base(Base_2)
c1.hello()
c2.hello()
main()
Result:
hello_1
hello_2
Works well in both python 2 and 3.
For more information, see the related question How to dynamically change base class of instances at runtime?