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
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)
I have a set of Point objects, and I would like to be able to remove these objects from my set. However, Python seems to be comparing these by pointer rather than by value, so I can't remove elements easily, since they don't have the same pointer because they are not the exact same object. This is a problem only with objects, not with primitives.
A simplified example of my problem:
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
a = set()
a.add(Point(0, 0))
b = Point(0, 0)
a.remove(b)
When run, this returns
Traceback (most recent call last):
File "example.py", line 9, in <module>
a.remove(b)
KeyError: <__main__.Point object at 0x7f6292376128>
(obviously, the specific pointer changes on each run).
I would prefer to have the element (0, 0) removed from a, leaving a to be the empty set.
If you tell Python how to compare these objects, this can work. Add two methods like:
Code:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
Test Code:
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 __hash__(self):
return hash((self.x, self.y))
a = set()
a.add(Point(0, 0))
b = Point(0, 0)
a.remove(b)
It does not appear that you have actually added Point b to the set 'a'.
According to the docs:
If the element(argument) passed to the remove() method doesn't exist, keyError exception is thrown.
I made this slight alteration and it didn't error out
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
a = set()
a.add(Point(0, 0))
b = Point(0, 0)
a.add(b)
a.remove(b)
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 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)