Home > Software design >  How to create a new object with one method being a combination of a dynamic number of instantiated s
How to create a new object with one method being a combination of a dynamic number of instantiated s

Time:09-22

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.

  • Related