Calculating Euclidean distance using Magic Methods in Python 3

后端 未结 4 508
广开言路
广开言路 2021-01-23 14:46

I have a code that calculates Euclidean distance for me:

class Point:
    \"\"\"A point in two-dimensional space.\"\"\"

    def __init__(self, x, y):
        se         


        
相关标签:
4条回答
  • 2021-01-23 15:11

    As has been mentioned, it may not be a good idea to use a Point class to represent vectors. It's ok in simple programs, but it can get confusing in more complex code. The usual practice is to make points immutable. But anyway...

    To do this Euclidean distance operation we can "abuse" the new __matmul__ magic method. This method is invoked by the @ operator. Here's a short demo based on your code. Notice that I'm using x and y as the attributes, there's no good reason to mark them as private.

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return "Point({}, {})".format(self.x, self.y)
    
        def __add__ (self, other ):
            return Point(self.x + other.x, self.y + other.y)
    
        def __sub__ (self, other ):
            return Point(self.x - other.x, self.y - other.y)
    
        def __pow__(self, p):
            return Point(self.x ** p, self.y **p)
    
        def __abs__(self):
            d = self ** 2
            return (d.x + d.y) ** 0.5
    
        def __matmul__(self, other):
            ''' Euclidean distance between self & other '''
            return abs(self - other)
    
    # Test
    
    a = Point(5, 6)
    b = Point(2, 2)
    print(a + b)
    print(a - b)
    print(a @ b)
    

    output

    Point(7, 8)
    Point(3, 4)
    5.0
    
    0 讨论(0)
  • 2021-01-23 15:14

    It does not make sense for the difference of two points to be a point. It seems the object you are trying to implement is in fact a vector.

    Then the distance corresponds to the norm of a vector, implemented with __abs__.

    class Vector:
        def __init__(self, *args):
            self._coords = args
    
        def __add__(self, other):
            return Vector(*[x + y for x, y in zip(self._coords, other._coords)])
    
        def __sub__(self, other):
            return Vector(*[x - y for x, y in zip(self._coords, other._coords)])
    
        def __abs__(self):
            """Euclidian norm of the vector"""
            return sum(x**2 for x in self._coords) ** (1 / 2)
    

    Example

    v1 = Vector(1, 3)
    v2 = Vector(4, -1)
    
    print(abs(v2 - v1)) # 5.0
    
    # Also works in higher dimensions
    v3 = Vector(1, -1, 0)
    v4 = Vector(4, 6, -2)
    
    print(abs(v3 - v4)) # 7.87
    
    0 讨论(0)
  • 2021-01-23 15:22

    Note that your definition of __pow__ is a bit nonstandard for vectors.

    But as it is, the distance of two points p1 and p2 could be written as sum((p1 - p2)**2)**.5. So we need your __pow__ method, your __sub__ method, and the only other addition is an __iter__ method which allows sum to work:

    class Point:
        """A point in two-dimensional space."""
    
        def __init__(self, x, y):
            self._x = x
            self._y = y
    
        def __eq__(self, other):
            return self._x == other._x and self._y == other._y
    
        def __sub__(self, other):
            return Point(self._x - other._x, self._y - other._y)
    
        def __pow__(self, power):
            return Point(self._x**power, self._y**power)
    
        def __iter__(self):
            yield self._x
            yield self._y
    
        def distance(self, other):
            return sum((self - other)**2)**.5
    
    p1 = Point(2, 3)
    p2 = Point(5, -1)
    
    print(p1.distance(p2))
    Out: 5.0
    

    That's the shortest way based on your existing code anyway. You could experiment further by adding a scalar multiplication method and an addition method and then defining sub as p1 + (-1)*p2. You can also make things a bit easier on yourself by implementing a __repr__ method.

    0 讨论(0)
  • 2021-01-23 15:27

    Your second class works for me. I do not see any issues with it:

    In [9]: class Point_1(object):
       ...:     
       ...:     def __init__(self, x, y):
       ...:         self._x = x
       ...:         self._y = y
       ...:   
       ...:     
       ...:     def setX(self, x,y):
       ...:         self._x = x
       ...:         self._y = y
       ...:  
       ...:     def getX(self):
       ...:         return self._x,self._y
       ...:     
       ...:     
       ...:     def __sub__ (self, other ):
       ...:         return Point_1(self._x - other._x, self._y - other._y)
       ...:     
       ...:     def __pow__(self,p):
       ...:         return Point_1(self._x ** p, self._y **p)
    

    And then:

    In [14]: p1 = Point_1(10,4)
        ...: print(p1.getX())
        ...: 
        ...: p2 = Point_1(3,1)
        ...: print(p2.getX())
        ...: 
        ...: p3 = p1 - p2
        ...: 
        ...: 
    (10, 4)
    (3, 1)
    
    In [15]: p3.getX()
    Out[15]: (7, 3)
    

    Call it vector or point or whatever, it seems to be doing what you want it to do.

    0 讨论(0)
提交回复
热议问题