Jack Le Jack Le - 2 months ago 6
Python Question

Best practice to implement multiple contructors in python

I'm pretty sure this has been asked many times but I'm still not sure how to implement the multiple constructors in Python. I know that in python, I can only have one constructor unlike in java or C# or C++. I'm still pretty new to it. Long story short, I need to implement a line object. The line will be represented by the function y = ax + b. So the only things I need to store in the line are a, b and a boolean for the special case where the line is vertical (a = infinity). In that case, a will store the x position of the line. To create a line, I have 3 ways. 1 is to directly put in a, b and the boolean. 2 is to put in 2 points in form of tuples. 3 is to put in a point and a vector. My code so far:

class line:
def __init__(self, a, b, noSlope):
self.a = a
self.b = b
self.noSlope = noSlope

def lineFromPoints(point1, point2):
deltaX = point2[0] - point1[0]
deltaY = point2[1] - point1[1]
if deltaX == 0:
return line(point1[0], 0, True)
else:
a = deltaY / deltaX
b = point1[1] - a * point1[0]
return line(a, b, False)

def lineFromVector(vector, point):
if vector[0] == 0:
return line(point1[0], 0, True)
else:
a = vector[1] / vector[0]
b = point1[1] - a * point1[0]
return line(a, b, False)


Not sure if there's a better way to do this

Answer

UPDATE:

The more pythonic way of doing multiple constructors is with @classmethod, as suggested by Jim. Raymond Hettinger did a talk on Python's class development toolkit at Pycon 2013, where he talked about multiple constructors using @classmethod.

class Line:
    def __init__(self, a, b, noSlope):
        self.a = a
        self.b = b
        self.noSlope = noSlope

    @classmethod
    def fromPoints(cls, point1, point2):
        deltaX = point2[0] - point1[0]
        deltaY = point2[1] - point1[1]
        if deltaX == 0:
            return cls(point1[0], 0, True)
        else:
            a = deltaY / deltaX
            b = point1[1] - a * point1[0]
            return cls(a, b, False)

    @classmethod
    def fromVector(cls, vector, point):
        if vector[0] == 0:
            return cls(point1[0], 0, True)
        else:
            a = vector[1] / vector[0]
            b = point1[1] - a * point1[0]
            return cls(a, b, False)


line = Line.fromPoints((0,0), (1,1))

Similar to self, the cls parameter for the @classmethod functions is implicitly passed as the calling class (in the example above, it would be Line). This is used to accommodate future subclasses using the additional constructors; it removes potential bugs from accidentally bypassing a subclass's implementation of a constructor by hard coding the base class in place of cls.


ORIGINAL POST:

If you want to enforce the use of your constructors, you can make them static methods, and have them return an instance of your class.

class line:
    def __init__(self, a, b, noSlope):
        self.a = a
        self.b = b
        self.noSlope = noSlope

    @staticmethod
    def lineFromPoints(point1, point2):
        deltaX = point2[0] - point1[0]
        deltaY = point2[1] - point1[1]
        if deltaX == 0:
            return line(point1[0], 0, True)
        else:
            a = deltaY / deltaX
            b = point1[1] - a * point1[0]
            return line(a, b, False)

    @staticmethod
    def lineFromVector(vector, point):
        if vector[0] == 0:
            return line(point1[0], 0, True)
        else:
            a = vector[1] / vector[0]
            b = point1[1] - a * point1[0]
            return line(a, b, False)

# Create instance of class
myLine = line.lineFromPoints((0,0), (1,1))

EDIT:
If you want to force use of your constructors over the use of Line.__init__, you can use the following factory to hide direct instantiation of the Line class:

class LineFactory:
    class Line:
        def __init__(self, a, b, noSlope):
            self.a = a
            self.b = b
            self.noSlope = noSlope

    @staticmethod
    def fromPoints(point1, point2):
        deltaX = point2[0] - point1[0]
        deltaY = point2[1] - point1[1]
        if deltaX == 0:
            return LineFactory.Line(point1[0], 0, True)
        else:
            a = deltaY / deltaX
            b = point1[1] - a * point1[0]
            return LineFactory.Line(a, b, False)

    @staticmethod
    def fromVector(vector, point):
        if vector[0] == 0:
            return LineFactory.Line(point1[0], 0, True)
        else:
            a = vector[1] / vector[0]
            b = point1[1] - a * point1[0]
            return LineFactory.Line(a, b, False)

# Create line    
line = LineFactory.fromPoints((0,0), (1,1))
Comments