简体   繁体   中英

Calculating Euclidean distance using Magic Methods in Python 3

I have a code that calculates Euclidean distance for me:

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 distance(self, other):
        new_x = self._x - other._x
        new_y = self._y - other._y
        print(new_x,'  ',new_y)
        return (new_x ** 2 + new_y ** 2) ** 0.5

p1 = Point(10, 4)
p2 = Point(3, 1)


print('Euclidean distance : 'p1.distance(p2))

However, now I want to calculate this distance using magic methods in python like __sub__ and __pow__ . I've managed to implement __sub__ but I don't know how to implement for __pow__ and square root. This is my code so far:

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)




p1 = Point_1(10,4)
print(p1.getX())

p2 = Point_1(3,1)
print(p2.getX())

p3 = p1 - p2

How can I implement the rest of the formula using magic methods. I'm really confused. Help would me appreciated.

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

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

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.

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.

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