2

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

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jack Le
  • 311
  • 4
  • 8
  • As this is a python question, please remove the c# and c++ tags as it has nothing to do with your problem – BugFinder Mar 27 '16 at 19:44
  • Not sure what you're asking for specifically, and the constructor looks fine as is, but a couple of things that stand out - class names are always capitalized, so call it `class Line:`. Also for all those index lookups, (`point1[0]` etc.), you could say `x1, y1 = point1` and `x2, y2 = point2` and then your `deltaX` could just be `x2-x1` and later you could reuse `x1, x2, y1, y2`, etc. Basically think of your notation as how would I do this if I were trying to solve some similar math equation on paper. – Bahrom Mar 27 '16 at 19:58
  • "Better ways" yes, your code doesn't even work. Make `lineFromPoints()` and `lineFromVector()` static methods or just take them outside the class and that's all. – Stop harming Monica Mar 27 '16 at 20:03
  • @BAH can you explain what exactly `x1, y1 = point1` does? That might be very useful for this algorithm – Jack Le Mar 27 '16 at 20:13
  • @JackLe I'm assuming your `point1` is a tuple that contains two elements, say `(0, 1)`. `x1, y1 = point1` unpacks that tuple and gives you two variables, `x1 = 0, y1 = 1`, and, from that point on, you can just start using those variables instead of saying `point1[0], point1[1]` etc. – Bahrom Mar 27 '16 at 20:14
  • @BAH thanks. I never knew of that. Python is wonderful! – Jack Le Mar 27 '16 at 20:18
  • @JackLe See my edit for an additional way to implement the constructors. – Stewart Smith Mar 27 '16 at 20:20

3 Answers3

9

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))
Community
  • 1
  • 1
Stewart Smith
  • 1,396
  • 13
  • 28
0

You could create one constructor with all required parameters with default value None:

class line:
    def __init__(self, a = None, b = None, noSlope = None, point1 = None, point2 = None, vector = None, point = None):
        pass

Then you will check which paameters are passed in constructor and create line from these parameters.

Aleks Lee
  • 136
  • 1
  • 1
  • 10
0

You could use Pyhton's enums to declare the initialization method as follows:

from enum import Enum

class Line:

    class InitTypes(Enum):
        coefs, point_vector, point_point, vertical = range(4)


    def __init__(self, a, b, method):
        self.noSlope = False
        if method == Line.InitTypes.coefs:
            self.a = a
            self.b = b
        elif method == Line.InitTypes.point_vector:
            # Do math
            pass
        elif method == Line.InitTypes.point_point:
            # Do math
            pass
        elif method == Line.InitTypes.vertical:
            self.noSlope = True
            self.a = a
            self.b = b
        else:
            assert(False)

Then, create a line as follows:

x = Line((0,1), (3,4), Line.InitTypes.point_point)
BallpointBen
  • 9,406
  • 1
  • 32
  • 62