I'm trying to create a class that will allow me to add/multiply/divide objects of the same class together or add/multiply numeric arguments to each member of the class
So my class is for coordinates (I am aware there are great packages out there that do everything I want better than I could ever hope to on my own, but now I'm just curious).
class GpsPoint(object):
"""A class for representing gps coordinates"""
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
return GpsPoint(self.x + other.x, self.y + other.y, self.z + other.z)
def __radd__(self, other):
return GpsPoint(self.x + other, self.y + other, self.z + other)
def __str__(self):
return "%d, %d, %d" % (self.x, self.y, self.z)
This was my original attempt. I found it worked, but only if I used a numeric argument first
>>foo = GpsPoint(1,2,3)
>>print 5 + foo
6, 7, 8
>>print foo + 5
AttributeError: 'int' object has no attribute 'x'
So, what is the pythonic way to do this, is there a pythonic way, is this just silly? I see what the philosophical problem is with using isinstance() and I know I could toss in a try except block I'm just curious how I should go about this.
The "Pythonic" way is to "ask forgiveness rather than permission" - that is, instead of checking the type beforehand, try to add and, if it fails, catch the exception and deal with it, like so:
class GpsPoint(object):
"""A class for representing gps coordinates"""
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
try:
return GpsPoint(self.x + other.x, self.y + other.y, self.z + other.z)
except AttributeError:
return GpsPoint(self.x + other, self.y + other, self.z + other)
def __radd__(self, other):
try:
return GpsPoint(self.x + other.x, self.y + other.y, self.z + other.z)
except AttributeError:
return GpsPoint(self.x + other, self.y + other, self.z + other)
def __str__(self):
return "%d, %d, %d" % (self.x, self.y, self.z)
You are going to have to try to determine what type other is, at least to the extent that it's compatible with GpsPoint. If you can't figure it out then just return NotImplemented and the interpreter will try to handle it from there.
Short answer: use isinstance().
There is no other way to dermine the type of "other" in your methods. Also, if you check the sources of many python libraries you will find that there are lots of places where isinstance() is used. So this is just the state of art in python:-).
Related
This question already has answers here:
Python function overloading
(19 answers)
Closed 3 months ago.
Is it possible to overload magic methods or get a similar result as in e.g. overloading methods in C# for magic methods in python ? or is it simply another handicap of this language and it is impossible at this moment as in the past "types" used to be ;)
def __init__(self, x:float, y:float, z:float) -> None:
self.x = x
self.y = y
self.z = z
def __add__(self, other:'Vector') -> 'Vector':
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
def __add__(self, other:float) -> 'Vector':
return Vector(self.x + other, self.y + other, self.z + other)
I have tried to test it and...
vector_one = Vector(1, 2, 3)
vector_two = Vector(4, 5, 6)
print(vector_one + vector_two)
No, it's not possible to automatically overload, but you can check for type and proceed accordingly:
from typing import Union
class Vector:
# ...
def __add__(self, other: Union['Vector', float]) -> 'Vector':
if type(other) == Vector:
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
elif type(other) == float:
return Vector(self.x + other, self.y + other, self.z + other)
else:
raise TypeError # or something else
This question already has answers here:
Compare object instances for equality by their attributes
(16 answers)
Closed 1 year ago.
def main() :
a = Complex(3.0,-4.5)
b = Complex(4.0, -5.0)
c = Complex(-1.0, 0.5)
print(a+b)
print(a+b-c)
print(a-b)
print(a-b+c)
print(a-c)
print(b == (a-c))
class Complex:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Complex(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Complex(self.x - other.x, self.y - other.y)
def __str__(self):
return f"Complex({self.x}, {self.y})"
main()
I want to get the answer like this:
Complex(7.0,-9.5)
Complex(8.0,-10.0)
Complex(-1.0,0.5)
Complex(-2.0,1.0)
Complex(4.0,-5.0)
True
Everything is Okay until Complex(4.0, -5.0), but I got 'False' in the end. So I tried to debug and found <__main__.Complex object at 0x0397~~~> == <__main__.Complex object at 0x03E7~~~> (numbers after 'at' is different) so False is shown. I tried to print(a-c) and print(b) each and they look same when printed but something like address is different. What should I do to get True instead of False?
As pointed out in the comments you haven't defined the __eq__ operator which is used for equality comparisons (==). Since it's not defined python doesn't know how to compare these two and instead tries comparing their identity like the is keyword, which checks if they are the same objects (the address or the "number" after the at is the same).
Here is one implementation of __eq__
def main() :
a = Complex(3.0,-4.5)
b = Complex(4.0, -5.0)
c = Complex(-1.0, 0.5)
print(a+b)
print(a+b-c)
print(a-b)
print(a-b+c)
print(a-c)
print(b == (a-c))
class Complex:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Complex(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Complex(self.x - other.x, self.y - other.y)
def __str__(self):
return f"Complex({self.x}, {self.y})"
#returns True if the two objects are equal
def __eq__(self, other):
return self.x == other.x and self.y == other.y
main()
and you should get True as your final output
A quick (but weak) solution would be to change the line
print(b == (a-c))
into
print(str(b) == str(a-c))
However, if you wanted to implement more foolproof equality checks you should check out the dunder method __eq__. For example, you could add the method:
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
This question already has answers here:
User defined __mul__ method is not commutative
(2 answers)
Closed 2 years ago.
I have successfully implemented the inner product like this (its an excercise - some things might beeing imported):
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Point({}, {})'.format(self.x, self.y)
def __add__(self, point):
return Point(self.x + point.x, self.y + point.y)
def __sub__(self, point):
return Point(self.x - point.x, self.y - point.y)
def __mul__(self, second):
if isinstance(second, Point):
return (self.x * second.x + self.y * second.y)
elif isinstance(second, int):
return Point(self.x * second, self.y * second)
elif isinstance(second, float):
return Point(self.x * second, self.y * second)
def __mul__(first, self):
if isinstance(first, int) and isinstance(self, Point):
return Point(self.x * first, self.y * first)
elif isinstance(first, float) and isinstance(self, Point):
return Point(self.x * first, self.y * first)
I get an error if I execute say
p=Point(3.7,4.8)
print(p*3)
Without the last block (scalar mult. from the left) the outout is correct.
Surely there is an error in the last block cause
p=Point(3.7,4.8)
print(3*p)
(which the last block is for) also is not working.
Hope someone can help - thanks!
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Point({}, {})'.format(self.x, self.y)
def __add__(self, point):
return Point(self.x + point.x, self.y + point.y)
def __sub__(self, point):
return Point(self.x - point.x, self.y - point.y)
def __mul__(self, second):
if isinstance(second, Point):
return (self.x * second.x + self.y * second.y)
elif isinstance(second, int) or isinstance(second, float):
return Point(self.x * second, self.y * second)
def __rmul__(first, self):
# no need to confirm that self is an instance of Point
# since we're inside a method of class Point anyway
if isinstance(first, int) or isinstance(first, float):
return Point(self.x * first, self.y * first)
p=Point(3.7,4.8)
print(p*3)
print(p*3.5)
print(3*p)
print(3.5*p)
leads to output
Point(11.100000000000001, 14.399999999999999)
Point(12.950000000000001, 16.8)
None
None
I see, you have mixed up the arguments in the definition of __rmul__. self should always be the first argument (because it is implicitly passed as the first argument), so instead of
def __rmul__(first, self):
write
def __rmul__(self, first):
When you get an error or unexpected output with certain commands, please make sure to include that error/output so other people can better understand what's going on.
In your code, you've defined __mul__ twice; thus the second definition overwrites the first one. I think what you may have meant was to define __mul__ and also __rmul__. Additionally, you're introducing some unnecessary conditions in your if statements. Here's a suggestion:
def __mul__(self, second):
if isinstance(second, Point):
return (self.x * second.x + self.y * second.y)
elif isinstance(second, int) or isinstance(second, float):
return Point(self.x * second, self.y * second)
def __rmul__(self, first):
# no need to confirm that self is an instance of Point
# since we're inside a method of class Point anyway
if isinstance(first, int) or isinstance(first, float):
return Point(self.x * first, self.y * first)
That should solve it, let me know if it doesn't.
The problem is to implement scalar and inner product in the vector class in Python. Here is the code:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Point(%s, %s)' % (self.x, self.y)
def __mul__(self,other):
x, y = self.x*other.x, self.y*other.y
return self.__class__(x,y)
def __rmul__(self,other):
x,y = other*self.x,other*self.y
return self.__class__(x,y)
def __add__(self,other):
x,y = self.x + other.x, self.y + other.y
return self.__class__(x, y)
def __sub__(self,other):
x,y = self.x - other.x, self.y - other.y
return self.__class__(x, y)
With inner product it works great, but with scalar multiplication(like if I call Point(3,2)*2) it gives the following error:
AttributeError: 'int' object has no attribute 'x'.
How do I fix this?
Look carefully at your __mul__ method:
def __mul__(self,other):
x, y = self.x*other.x, self.y*other.y
return self.__class__(x,y)
When you're trying to multiply a Point and an int, you're passing the int as the second argument (to the other parameter) to Point.__mul__. Then your method will try to access other.x and other.y, and an int doesn't have these attributes:
You can manually check for the type of other and decide whether you should be doing scalar or vector product:
def __mul__(self,other):
if isinstance(other, Point):
x, y = self.x*other.x, self.y*other.y
return self.__class__(x,y)
elif isinstance(other, (int, float, complex)):
x, y = other * self.x, other * self.y
return self.__class__(x, y)
else:
raise TypeError
def __rmul__(self, other):
return self * other
Also, the way you're doing multiplication is strange. A dot product of two vectors should be a number, not a vector.
https://en.wikipedia.org/wiki/Dot_product
You need to add a check for the type of other, it can be a Point instance or other
Here the code for __mul__
def __mul__(self, other):
if isinstance(other, Point):
return Point(self.x * other.x, self.y * other.y) # Point * Point
return Point(self.x * other, self.y * other) # Point * othertype
def __rmul__(self, other):
return Point(self.x * other, self.y * other) # othertype * Point
class Point(object):
''' A point on a grid at location x, y '''
def __init__(self, x, y):
self.X=x
self.Y=y
def __str__(self):
return "X=" + str(self.X), "Y=" + str(self.Y)
def __add__(self, other):
if not isinstance(other, Point):
raise TypeError("must be of type point")
x= self.X+ other.X
y= self.Y+ other.Y
return Point(x, y)
p1= Point(5, 8)
print p1 + [10, 12]
When trying to add list or tuple at RHS i.e. print p1 + [10, 12], I'm getting
attributeError: int object has no attribute
How can this problem be solved?
First of all I can't reproduce the exact error you show, but I believe that is some sort of a "typo". You are trying to add a list instance to a Point instance, while the __add__ method of the later throws the error whenever you try to add anything that is not a Point instance.
def __add__(self, other):
if not isinstance(other, Point):
raise TypeError("must be of type point")
You could possibly overcome it by adding a fair bit of polymorphism.
from collections import Sequence
class Point(object):
...
def _add(self, other):
x = self.X + other.X
y = self.Y + other.Y
return Point(x, y)
def __add__(self, other):
if isinstance(other, type(self)):
return self._add(other)
elif isinstance(other, Sequence) and len(other) == 2:
return self._add(type(self)(*other))
raise TypeError("must be of type point or a Sequence of length 2")
You may have a comma instead of a plus. Look at
def __str__(self):
return "X=" + str(self.X), "Y=" + str(self.Y)
Which should be
def __str__(self):
return "X=" + str(self.X) + ", Y=" + str(self.Y)
At least on python3 when I correct it your code runs nicely. Obviously using print(p1 + Point(10,12)).