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 !
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