'How to make the class constructor that returns the object of child class?

I'm coding in Python. I have a Base class which contains several methods. There are some child classes; they may have their own methods. I want the Base class constructor to create the object not of the class itself but the object of one of the child classes depending on the argument.

For example, guess our child classes are Point, Line, and Plane, all are inherited from Base, and the difference between them is set by the dim attribute of the Base class.

class Base():
    
    def __init__(self, dim, a, b):
        self.dim = dim
        self.a = a
        self.b = b
        
class Point(Base):
    
    def __init__(self, a, b):
        super().__init__(1, a, b)
        
class Line(Base):
    
    def __init__(self, a, b):
        super().__init__(2, a, b)
        
class Plane(Base):
    
    def __init__(self, a, b):
        super().__init__(3, a, b)

If I explicitly create a Point, the object type will be Point:

pointA = Point(0, 0)
type(pointA) # __main__.Point

But if I do the same through the Base constructor, the object will be of class Base:

pointB = Base(1, 0, 0)
type(pointB) # __main__.Base

So I'd like to change this behavior and make the Base constructor return a Point, Line or Plane object if the dim attribute is equal to 1, 2 or 3 respectively. How can I do this?

EDIT: Based on this thread (Improper use of __new__ to generate class instances?) I overrid the Base.__new__() and got the following code:

class Base():
    
    def __new__(cls, a, b, dim):
        if dim == 1:
            return object.__new__(Point)
        elif dim == 2:
            return object.__new__(Line)
        elif dim == 3:
            return object.__new__(Plane)    
        
class Point(Base):
    
    def __init__(self, a, b, dim):
        self.a = a
        self.b = b
        
class Line(Base):
    
    def __init__(self, a, b, dim):
        self.a = a
        self.b = b
        
class Plane(Base):
    
    def __init__(self, a, b, dim):
        self.a = a
        self.b = b

The code above works, but it requires an explicit setting of the dim argument even when I create a new Point instance. A non-identical set of arguments in Base.__new__() and Point.__init__() raises an error. How can I keep this behavior but remove dim from the Point constructor?



Solution 1:[1]

You don't typically want to do this by instantiating a base case. While I suppose you could override __new__, I wouldn't advise it for this. Notably, though, __init__ has a None return type. So, regardless, you can't do this in __init__ - the object is already created at that point.

Instead, what you probably want is a static so-called 'Factory' method on your base class:

from typing import Type

class Base():

    @staticmethod
    def create(dim, a, b) -> Type[Base]:
        # Decide which subclass you want to create, instantiate and return it.

...
new_obj = Base.create(x, y, z)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1