I have a simple vector class that overloards several arithmetic operators:
class vec2:
x = 0.0
y = 0.0
def __add__(self,other):
self.x = other.x
self.y = other.y
def __mul__(self,scalar):
self.x *= scalar
self.y *= scalar
However, somewhere else I call the method like this:
class foo:
position = vec2()
velocity = vec2()
def update(self,dt):
self.position += self.velocity * dt;
However, once I get to the update function, the interpreter gives an error:
'tuple' object has no attribute 'x'
inside the __add__ function.
Why is "other" in __add__ passed as a tuple, and not a vec2?
The entire code is here.
Return new vectors when using __add__ and __mul__, and handle 'strange' types:
class vec2:
x = 0.0
y = 0.0
def __init__(self, x=0.0, y=0.0):
self.x, self.y = x, y
def __add__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
result = self.__class__(self.x, self.y)
result.x += other.x
result.y += other.y
return result
def __iadd__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
self.x += other.x
self.y += other.y
return self
def __mul__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
result = self.__class__(self.x, self.y)
result.x *= other.x
result.y *= other.y
return result
def __imul__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
self.x *= other.x
self.y *= other.y
return self
To modify the vectors in-place, use __iadd__ and __imul__; these still need to return the new value; this can be self.
Note that this does not handle just passing in a tuple of (x, y) coordinates. If you want to support that usecase, you need to specially handle it:
class foo:
def __init__(self, position=(0.0, 0.0), velocity=(1.0, 1.0)):
self.position = vec2()
self.velocity = vec2(*velocity)
def update(self, dt):
if isinstance(dt, tuple):
dt = vec2(*dt)
self.position += self.velocity * dt;
Note also that you should not really use class attributes for your position and velocity values; I've used instance attributes instead above, and took the opportunity to set both position and velocity to sane values.
Demo:
>>> f = foo()
>>> f.position.x, f.position.y
(0.0, 0.0)
>>> f.update((1, 2))
>>> f.position.x, f.position.y
(1.0, 2.0)
Related
This question already has answers here:
Types that define `__eq__` are unhashable?
(4 answers)
Closed 3 months ago.
I have a base class and a subclass, such as:
class Base:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self.x == other.x
def __hash__(self):
return hash(self.x)
class Subclass(Base):
def __init__(self, x, y):
super().__init__(x)
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
Since the parent class implements __hash__, it should be hashable. However, when I try to put two copies in a set, such as {Subclass(1, 2), Subclass(1, 3)}, I get this error:
TypeError: unhashable type: 'Subclass'
I know if an object implements __eq__ but not __hash__ then it throws the TypeError, but there is a clearly implemented hash function. What's going on?
The __eq__ rule applies both to classes without any subclasses implementing __hash__ and to classes that have a parent class with a hash function. If a class overrides __eq__, it must override __hash__ alongside it.
To fix your sample:
class Base:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self.x == other.x
def __hash__(self):
return hash(self.x)
class Subclass(Base):
def __init__(self, x, y):
super().__init__(x)
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
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)
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)).
I have a class Point with xand y attributes. I'd like to get False comparing a Point object with any other type of object. For instance, Point(0, 1) == None fails:
AttributeError: 'NoneType' object has no attribute 'x'
The class:
class Point():
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 __ne__(self, other):
return not self.__eq__(other)
How do I configure __eq__to get False in comparison with any other object type?
I would check to see whether the other object acts like a Point object instead of rejecting all non-Point objects:
def __eq__(self, other):
try:
return self.x == other.x and self.y == other.y
except AttributeError:
return False
That way Point(1, 1) == Vector(1, 1), in case you use coordinate vectors.
def __eq__(self, other):
if not isinstance(other, Point):
return False
try:
return self.x == other.x and self.y == other.y
except AttributeError:
return False
First check the type and return False if its not a Point instance. We do this in case they are comparing some other type that happens to have an x or y attribute but isn't necessarily the same context.
Second catch an attribute error, just in case someone subclasses Point and removes the attribute or changes Point in some way.
Try this:
def __eq__(self, other):
return isinstance(other, Point) and self.x == other.x and self.y == other.y