Search code examples
pythoninheritancefluent-interface

Python 3: Returning a new child class instance from an inherited method, when child constructor has more arguments than parent constructor


Suppose I have a parent class and multiple child classes, which extend the parent class by including more specific information about the ideas they represent. For example:

class Shape:
    def __init__(self, center):
        self.center = center


class Square(Shape):
    def __init__(self, center, side_length):
        super().__init__(self, center)
        self.side_length = side_length
        self.area = side_length ** 2


class Circle(Shape):
    def __init__(self, center, radius):
        super().__init__(self, center)
        self.radius = radius
        self.area = 3.14 * (radius ** 2)

Suppose I want to implement a method such as translate(new_center) in the parent class, which would return a new object with a different center location than the original object. Because all child classes should behave the same way (i.e. the attribute self.center should change), it makes sense to implement translate() as a method of the parent class Shape.

If I want to return a new object of type Shape every time translate() is called, we can simply define translate() as a method of Shape like so:

    def translate(self, new_center):
        return Shape(new_center)

However, if any child class instances call this method, the result will be of type Shape, and thus any additional state information contained by the original instance, such as side_length and area for a Square, will be lost. Additionally, translate() cannot be defined as

    def translate(self, new_center):
        return self.__class__(new_center)

because the constructors for each of the child classes require additional arguments that the parent class constructor doesn't. How can I implement this without having to override the parent method in each of the child classes (avoiding which was the entire point of defining the parent method)?


Solution

  • You could copy the object and modify the copy:

    import copy
    
    class Shape():
      def __init__(self, center):
        self.center = center
    
      def translate(self, new_center):
        new_shape = copy.copy(self) # Replace with deepcopy if needed
        new_shape.center = new_center
    
    ...