Search code examples
pythonpython-3.xclassinheritancecomposition

Is there anyway to override class from other module while keeping it's methods in python


In order to illustrate my question, here are 3 modules :

This is the module A

'''
lets call this module the parent, it regroups multiple classes with several
method each
'''

class Rectangle():
    '''
    The basic rectangle class that is parent for other classes in this
    module
    '''
    def __init__(self, x_length, y_length):
        self.x_length = x_length
        self.y_length = y_length

    def create_points(self):
        '''
        Basic geometrical method 
        '''
        self.point_1 = [x_length/2, y_length/2]
        self.point_2 = [-x_length/2, y_length/2]
        self.point_3 = [-x_length/2, -y_length/2]
        self.point_4 = [x_length/2, -y_length/2]

class Square(Rectangle):
    '''
    The square that is a rectangle with two identical sides
    '''
    def __init__(self, side_dim):
        super().__init__(side_dim, side_dim)

class SquareCollection():
    '''
    Creates a composition relation with an other class of the module
    '''
    def __init__(self, dim_list):
        '''
        The constructor creates a square for every float in the given list
        '''
        for val in dim_list:
            try:
                self.element.append(Square(val))
            except AttributeError:
                self.element = [Square(val)]

    def create_points(self):
        for elmt in self.element:
            elmt.create_points()

This is the Module B1

'''
lets call this module the child 1, it redefines the classes from the parent but
with an additionnal method specific for its use case
'''
import Module_A as ma

class Rectangle(ma.Rectangle):
    '''
    The basic rectangle class that is parent for other classes in this
    module
    '''
    def module_specific_method(self):
        self.special_attribute_1 = True

class Square(ma.Square):
    '''
    The square that is a rectangle with two identical sides
    '''
    def module_specific_method(self):
        self.special_attribute_1 = True

class SquareCollection(ma.SquareCollection):
    '''
    Redefining the SquareCollection with an additionnal method
    '''
    def module_specific_method(self):
        for elmt in self.element:
            elmt.module_specific_method()

And this is the module B2

'''
lets call this module the child 2, it redefines the classes from the parent but
with an additionnal method specific for its use case
This module is a copy of Module_B2 and is used to justify the used code
structure
'''
import Module_A as ma

class Rectangle(ma.Rectangle):
    '''
    The basic rectangle class that is parent for other classes in this
    module
    '''
    def module_specific_method(self):
        self.special_attribute_2 = True

class Square(ma.Square):
    '''
    The square that is a rectangle with two identical sides
    '''
    def module_specific_method(self):
        self.special_attribute_2 = True

class SquareCollection(ma.SquareCollection):
    '''
    Redefining the SquareCollection with an additionnal method
    '''
    def module_specific_method(self):
        for elmt in self.element:
            elmt.module_specific_method()

When I import the module B2 create a sc = SquareCollection([4, 3.5, 0.8]) instance and then run sc.module_specific_method() I get an AttributeError for the called Square classes that are created using the A module because the SquareCollection class inherits from the one defined in the A module which itself creates multiple Square instances based on the class defined in the same module. The AttributeError is expected because nowhere in the A module I am defining this module_specific_method.

Due to the structure of my code and how I am currently using it, I am using either module B1 or module B2. I currently avoid this issue by rewriting everything contained in module A twice (one in B1 and the other in B2). Because I am refactoring all my code, I wish I could remove all code duplicate and put it in a "common" module as shown above with the simplified example.

How is it possible for classes in module A to inherit/point back to the B module from which I called it ?

I hope my issue is clear, because I really have a hard time formalizing it.

Cheers !


Solution

  • You can achieve this by making the Square class used in the base SquareCollection an attribute of the collection class, so that instead of being hardcoded it becomes explicitly overridable by subclasses, too:

    # Module_A
    class Square:
        pass      # implement stuff here
    
    class SquareCollection
        BaseItem = Square    # the "class that is collected here"
    
        def __init__(self, dim_list):
            # spawn BaseItem instances here, which are Square by default,
            # but might be set to something else in a subclass
            self.element = [self.BaseItem(val) for val in dim_list]
    
    # Module_B1
    import Module_A as ma
    
    class Square(ma.Square):
         pass
    
    class SquareCollection(ma.SquareCollection):
        # override the collection's BaseItem with this module's Square class,
        # but keep the rest of the SquareCollection code the same
        BaseItem = Square