简体   繁体   中英

What is the equivalent of java's 'constructor-overload' in python?

I'm currently studying Python. I was trying to write an exercise code making a 'Line'-class with different possible input-parameters and hence different constructors, as one can do in java. ie a constructor with (point,point) objects and a constructor with (point,slope) It works with both __init__ methods separately, but not with both as it does in python. Could anybody help me going with this.

class Point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def distance_to(self,p):
        return((p1.x-p.x)**2+(p1.y-p.y)**2)**(1/2)

class Line:
    def __init__(self, p1,p2):
        self.p1=p1
        self.p2=p2
    def __init__(self,p1,a):
        self.p1=p1
        self.a=a
        b=self.p1.y-a*p1.x
        p2=Point(0,b)
        self.p2=p2             
    def slope(self):
        return (p2.y-p1.y)/(p2.x-p1.x)
    def b_intersect(self):
        b_intersect=self.p1.y-self.slope()*p1.x
    def __str__(self):       
        b_intersect=self.p1.y-self.slope()*p1.x               
        if b_intersect>0:
            return 'y={}x+{}'.format(self.slope(),b_intersect)
        elif b_intersect<0:
            return 'y={}x{}'.format(self.slope(),b_intersect)
        elif b_intersect==0:
            return 'y={}x'.format(self.slope())

You can use *kwargs in __init__ method to check what arguments you received and act accordingly. Your code modified should look like this.

class Point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def distance_to(self,p):
        return((p1.x-p.x)**2+(p1.y-p.y)**2)**(1/2)

class Line:
    def __init__(self, p1,**kwargs):
        self.p1=p1
        if kwargs.get("p2") is not None:
            self.p2=p2
        elif kwargs.get("a")is not None:
            self.a=a
            b=self.p1.y-a*p1.x
            p2=Point(0,b)
            self.p2=p2  
        else:
           raise Exception("Did not give enough parameters")           
    def slope(self):
        return (p2.y-p1.y)/(p2.x-p1.x)
    def b_intersect(self):
        b_intersect=self.p1.y-self.slope()*p1.x
    def __str__(self):       
        b_intersect=self.p1.y-self.slope()*p1.x               
        if b_intersect>0:
            return 'y={}x+{}'.format(self.slope(),b_intersect)
        elif b_intersect<0:
            return 'y={}x{}'.format(self.slope(),b_intersect)
        elif b_intersect==0:
            return 'y={}x'.format(self.slope())

In Python, it's customary to write __init__ as the low-level initializer, which can also double as the primary "constructor". Additional constructors are defined as class methods.

class Line:
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    @classmethod
    def from_point_slope(cls, p1, a):
        b = self.p1.y - a * p1.x
        p2 = Point(0, b)
        return cls(p1, p2)

   # ...

Now Line has two constructors:

  1. Line.__new__ takes two Point objects. (It's rare to actually override __new__ ; the default of calling the parent's __new__ and using __init__ on the result is nearly always sufficient.)

  2. Line.from_point_slope takes a Point object and a slope. It calculates a second point on the line from the first Point and the slope, then pass them to Line.__new__ to create the new Line .

Both constructors use Line.__init__ to actually set the two attributes p1 and p2 .

In the future, if you think of more ways to define a line (for example, as the intersection of two planes), you simply define new class methods, rather than complicating __init__ further.

IMHO there are two rather simple ways to allow more than one construction syntax in Python.

Assuming that you are trying to mimic the following pair of constructors in Java:

class Line {
    public Line(Point p1, Point p2) {
        ...
    }
    public Line(Point p1, float a) {
        ...
    }
    ...
}
  1. The parameter type test (for the simplest use cases):

     class Line: def __init__(self, p1, p2): if isinstance(p2, Point): # ok, initialize the Line object from 2 points ... else: slope = p2 # ok, initialize the line object from 1 point and 1 float ...
  2. The parameter name test for more complex use cases:

     class Line: def __init__(self, p1, p2=None, slope=None): # test whether syntax is Ok (optional) if (p2 is None and slope is None) or (p2 is not None and slope is not None): raise ValueError("Either p2 or slope must be given") if slope is None: # ok, initialize the Line object from 2 points p1 and p2 ... else: # ok, initialize the line object from p1 and slope ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM