pythoninheritancemultiple-inheritance

How can I make a child class call a function in All of its children?


I'm having a little problem figuring the best way to make a "child" class to call function in it's children's

Design looks like this:

class BaseA():
    a_attr = 'I handle Table'
    def entery_point(self):
         print('Computing Value from %s' % self.a_attr) 
         for child in self._children:  #filling the self._children is where i think need help
             child().entery_point_b() 

class BaseB(A):
    b_attr = 'I handle Fields'
    def entery_point_b(self):
         print('Computing Value from %s and %s' % (self.a_attr,self.b_attr) )

#Now the concrete classes
class A(BaseA):
    a_attr = 'table_name'

class B(BaseB,A):
    b_attr = 'field_name_1'

class BB(BaseB, A):
    b_attr = 'field_name_2'


#another set of concrete classes to handle another table
class A2(BaseA):
    a_attr = 'another_table_name'

class B2(BaseB,A2):
    b_attr = 'another_field_name_1'

class BB2(BaseB, A2):
    b_attr = 'another_field_name_2'

if certain_condition:
    a = A()
    a.entery_point()
else:
    a2 = A2()
    a2.entery_point()

What I want to do here is that entery_point() get's executed for A then entery_point_b() for B & BB instance, and the same for the A2 instance (execute entery_point, then B2.entery_point_b() and BB2.entery_point_b() )

How I arrive to do that ?


What I have so far :

class BaseA():
    a_attr = 'I handle Table'
    def entery_point(self):
         print('Computing Value from %s' % self.a_attr) 
         for child in self._children:  #This is where i think need help
             child().entery_point_b() 

    def __init__(self):
            for x in self.__class__.__subclasses__():
                if x not in self._children:
                    self._children.append(x)

Problem here is that self._children is filled with all of the B,BB and B2 , BB2, Basicaly all classes derived from BaseA, What I need is it to be filled with just the classes derived from A (or A2)


The scenario Here:

Those classes form a "framework" that respond to CRUD operation in certain database tables with calculations & stuff to be stored in a cache table.

class BaseA handles the table level (creates a row in the cache table) , class BaseB handles the field level, ie each BaseB class populate a field in the row we just created with needed values.

Is it time for metaclass master?


Solution

  • It's where the self._children get declared.

    The above implementation actually work as expected !

    The problem was in my code , not the example above in the question.

    Here is the sad trick:

    self._children will be filled with ALL the sub classes of BaseA IF it's declared on a modular level , not in the init (which is something one shouldn't do). If it's declared in the init (as made 'implicit' in the example above) the flow would actually work as expected .

    Working implementation:

    class BaseA():
        a_attr = 'I handle Table'
        # _children = [] # & There was the problem...
    
        def entery_point(self):
             print('Computing Value from %s' % self.a_attr) 
             for child in self._children:  #This is where i think need help
                 child().entery_point_b() 
    
        def __init__(self):
            self._children=[] #the right way
            for x in self.__class__.__subclasses__():
                if x not in self._children:
                    self._children.append(x)
    class BaseB(A):
        b_attr = 'I handle Fields'
        def entery_point_b(self):
             print('Computing Value from %s and %s' % (self.a_attr,self.b_attr) )
    
    #Now the concrete classes
    class A(BaseA):
        a_attr = 'table_name'
    
    class B(BaseB,A):
        b_attr = 'field_name_1'
    
    class BB(BaseB, A):
        b_attr = 'field_name_2'
    
    
    #another set of concrete classes to handle another table
    class A2(BaseA):
        a_attr = 'another_table_name'
    
    class B2(BaseB,A2):
        b_attr = 'another_field_name_1'
    
    class BB2(BaseB, A2):
        b_attr = 'another_field_name_2'
    
    if certain_condition:
        a = A()
        a.entery_point()
    else:
        a2 = A2()
        a2.entery_point()