I am stuck on an inheritance/dynamic method creating problem. Assume I have an abstract base class and its children, like in this simplified example:
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
def __init__(self, x: int):
self.x = x
self.y = None
@abstractmethod
def evaluate(self, val: float) -> float:
pass
def get_y(self) -> float:
return self.y
class ChildClass1(MyBaseClass):
def evaluate(self, val: float) -> float
self.y = val*val
return val * self.x
class ChildClass2(MyBaseClass):
def evaluate(self, val: float) -> float
self.y = val**0.5
return val / self.x
# create some instances
c1 = ChildClass1(5)
c2 = ChildClass1(2)
c3 = ChildClass2(10)
c_list = [c1, c2, c3] # save them as a list
Now I want to create another class that is also a true child of my base class, but it's evaluate()
methods is a combination of other sub-class instances evaluate()
methods. To achieve this I tried writing a function that creates a instance of an instantiable version of the base class and assigning a new method to it:
class GeneralChildClass(MyBaseClass):
def evaluate(self, val: float) -> float:
pass
def combine_objects(object_list: list) -> GeneralChildClass:
new_c = GeneralChildClass(1) # initial parameter doesnt matter in this example
settattr(new_c, 'object_list', object_list)
def new_evaluate(self, val: float) -> float:
result = 0.0
new_y = 0
for c in self.object_list:
result = c.evaluate(val)
new_y = c.y
result = results / len(self.object_list)
self.y = new_y
return result
new_c.evaluate = new_evaluate.__get__(new_c, GeneralChildClass)
return new_c
c4 = combine_objects(c_list)
c4.evaluate(4.5)
c4.get_y()
c5 = combine_objects([c4, c1]) # this should also be possible
By using _get__()
the new 'merged' evaluation()
function can be added to the new object instance. However I am not sure if this is conceptually the correct/good.
Any kind of feedback on the general structure of my solution to the presented problem is welcome!
CodePudding user response:
There are a few things I would want to change about your design:
First have a different abstract base class which encapsulates the real base parts (self.x
is not one of them). Second you can have an intermediate base which sets up the self.x
Lastly a composite class.
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
def __init__(self):
self.y = None
@abstractmethod
def evaluate(self, val: float) -> float:
pass
def get_y(self) -> float:
return self.y
class ChildBase(MyBaseClass):
def __init__(self, x: int):
super().__init__()
self.x = x
class ChildClass1(ChildBase):
def evaluate(self, val: float) -> float
self.y = val*val
return val * self.x
class ChildClass2(ChildBase):
def evaluate(self, val: float) -> float
self.y = val**0.5
return val / self.x
class Composite(MyBaseClass):
def __init__(self, object_list):
super().__init__()
self.object_list = object_list
def evaluate(self, val: float) -> float:
result = 0.0
new_y = 0
for c in self.object_list:
result = c.evaluate(val)
new_y = c.y
result = result / len(self.object_list)
self.y = new_y
return result
# create some instances
c1 = ChildClass1(5)
c2 = ChildClass1(2)
c3 = ChildClass2(10)
c_list = [c1, c2, c3] # save them as a list
c5 = Composite(c_list)
print(c5.evaluate(4))
Also note that your self.y
needs some attention.