Why is it possible to override both __eq__ and __ne__ in classes? - python

Python provides the feature of overloading == operator AND != operator. But, why? Isn't it enough to overload == and != is automatically the opposite (in truth value) of ==?
Shouldn't the result of one imply the other automatically?

You don't have to, python isn't forcing you to. In fact, the documentation explains the what and why:
By default, __ne__() delegates to __eq__() and inverts the result
unless it is NotImplemented. There are no other implied relationships
among the comparison operators, for example, the truth of (x<y or x==y) does not imply x<=y.
In general, the truth of x==y does not have need to imply that x!=y is false. If your data model needs to reflect this relationship, python lets you do so with minimal headache.
Note that for earlier versions of python, not even this relationship was implied. For example,
class Foo:
def __init__(self, val):
self.val = val
def __eq__(self, other):
return self.val == other.val
f1, f2 = Foo(1), Foo(1)
Now, f1 == f2 returns True on any version. HOWEVER, f1 != f2 returns False on python-3.x, but True on python-2.x (because __ne__ is not implied on python-2.x, and in general two user defined objects are not equal if their IDs are not the same, i.e., not the same object).

Related

Override python's float for inexact comparison - hash implementation

I run into an interesting problem - here is an override of float that compares inexactly:
class Rounder(float):
"""Float wrapper used for inexact comparison."""
__slots__ = ()
def __hash__(self):
raise NotImplementedError
def __eq__(self, b, rel_tol=1e-06, abs_tol=1e-12):
"""Check if the two floats are equal to the sixth place (relatively)
or to the twelfth place (absolutely)."""
try:
return abs(self - b) <= max(rel_tol * max(abs(self), abs(b)),
abs_tol) # could use math.isclose
except TypeError:
return NotImplemented
...
The requirement is that equal objects have equal hashes - but can't seem to to come up with a formula that represents all Rounder(float) instances that would compare the same (so map them all to the same hash value). Most of the advice in the web is on how to define hash/equals for classes that compare based on some (immutable) attributes - does not apply to this case.
There is no valid way to hash these objects. Hashing requires a transitive definition of ==, which your objects do not have. Even if you did something like def __hash__(self): return 0, intransitivity of == would still make your objects unsafe to use as dict keys.
Non-transitivity is one of the big reasons not to define == this way. If you want to do a closeness check, do that explicitly with math.isclose. Don't make that operation ==.

Why does my equal method always return false?

Hi I just started learning classes in python and I'm trying to implement an array based list. This is my class and the init constructor.
class List:
def __init__(self,max_capacity=50):
self.array=build_array(max_capacity)
self.count=0
However, I wrote a method equals that returns true if the list equals another. However, it always return false. And yes my append method is working.
def __eq__(self,other):
result=False
if self.array==other:
result=True
else:
result=False
return result
This is how I tested it but it return false?
a_list=List()
b_list=[3,2,1]
a_list.append(3)
a_list.append(2)
a_list.append(1)
print(a_list==b_list)
Any help would be appreciated!
EDIT:
After all the helpful suggestions, I figured out I have to iterate through other and a_list and check the elements.
__eq__, for any class, should handle three cases:
self and other are the same object
self and other are compatible instances (up to duck-typing: they don't need to be instances of the same class, but should support the same interface as necessary)
self and other are not comparable.
Keeping these three points in mind, define __eq__ as
def __eq__(self, other):
if self is other:
return True
try:
return self.array == other.array
except AttributeError:
# other doesn't have an array attribute,
# meaning they can't be equal
return False
Note this assumes that a List instance should compare as equal to another object as long as both objects have equal array attributes (whatever that happens to mean). If that isn't what you want, you'll have to be more specific in your question.
One final option is to fall back to other == self to see if type of other knows how to compare itself to your List class. Equality should be symmetric, so self == other and other == self should produce the same value if, indeed, the two values can be compared for equality.
except AttributeError:
return other == self
Of course, you need to be careful that this doesn't lead to an infinite loop of List and type(other) repeatedly deferring to the other.
You are comparing the array embedded in your instance (self.array) to the entirety of the other object. This will not work well.
You need to compare self.array to other.array and/or convert both objects to the same type before comparing. You probably also need to specify what it means to compare two arrays (i.e., you want a single boolean value that indicates whether all elements are equal, not an array of boolean values for each element).
For the code below, I assume you are using a numpy ndarray for self.array. If not, you could write your own array_equal that will convert other to an array, then compare the lengths of the arrays, then return (self.array==other_as_array).all().
If you want to test for strict equality between the objects (same types, same values), you could use this:
from numpy import array_equal
import numpy as np
class List
...
def __eq__(self, other):
return isinstance(other, List) and array_equal(self.array, other.array)
If you just want to check for equality of the items in the list, regardless of the object type, then you could do this:
def __eq__(self, other):
if isinstance(other, List):
return array_equal(array, other.array)
else:
return array_equal(self.array, other)

What's the order of __hash__ and __eq__ evaluation for a Python dict?

I'm trying to understand what Python dictionaries must be doing internally to locate a key. It seems to me that hash would be evaluated first, and if there's a collision, Python would iterate through the keys till it finds one for which eq returns True. Which makes me wonder why the following code works (test code only for understanding the internals):
class MyClass(object):
def __eq__(self, other):
return False
def __hash__(self):
return 42
if __name__=='__main__':
o1 = MyClass()
o2 = MyClass()
d = {o1: 'o1', o2: 'o2'}
assert(o1 in d) # 1
assert(d[o1]=='o1') # 2
assert(o2 in d) # 3
assert(d[o2]=='o2') # 4
Shouldn't the dictionary be unable to find the correct key (returning either 'o1' or 'o2' in both cases #2 and #4, or throwing an error, depending on internal implementation). How is it able to land on the correct key in both cases, when it should never be able to correctly 'equate' the keys (since eq returns False).
All the documentation I've seen on hashing always mentions hash and eq together, never cmp, ne etc, which makes me think these 2 are the only ones that play a role in this scenario.
Anything you use as a dict key has to satisfy the invariant that bool(x == x) is True. (I would have just said x == x, but there are reasonable objects for which that isn't even a boolean.)
The dict assumes that this will hold, so the routine it uses to check key equality actually checks object identity first before using ==. This preliminary check is an implementation detail; you should not rely on it happening or not happening.
Reasonable objects for which (x == x) is not True include float('nan') and numpy.array([1, 2]).

Python Object Comparisons

I need to override object comparisons so that I can compare instances of classes using < > == != <= >= operators. Does python have any methods that I use to do so.
What Im not sure about is what methods I need to use to compare the vectors. I tried googling it but couldn't find anything. Does python have some methods to override < > <= and so on?
Yes, Python has magic methods which are exactly for this purpose. You're already using magic methods, such as __len__, __iter__, etc. Here's a good link for the comparison magic methods, you're particularly looking for:
__eq__ = equal to (==)
__ne__ = not equal to (!=)
__lt__ = less than (<)
__le__ = less than or equal to (<=)
__gt__ = greater than (>)
__ge__ = greater than or equal to (>=)
The magic methods in question are:
__lt__ -> less than
__le__ -> less than or equal
__ge__ -> greater than or equal
__gt__ -> greater than
and, of course,
__eq__ -> equal to
__ne__ -> not equal to
The basic framework for each of those methods is:
def __xx__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
# working code here
Those first two lines (after the def) will ensure that your objects work correctly with subclasses as well as other random objects.

Why is there a not equal operator in python [duplicate]

This question already has answers here:
Why does Python have an __ne__ operator method instead of just __eq__?
(3 answers)
Closed 7 years ago.
I was wondering about the reason of having a not equal operator in python.
The following snipped:
class Foo:
def __eq__(self, other):
print('Equal called')
return True
def __ne__(self, other):
print('Not equal called')
return True
if __name__ == '__main__':
a = Foo()
print(a == 1)
print(a != 1)
print(not a == 1)
outputs:
Equal called
True
Not equal called
True
Equal called
False
Doesn't this actually invite a lot of trouble by potentially saying:
A == B and A != B
can be correct at the same time. Furthermore this introduces a potential pitfall when forgetting to implement __ne__.
Depending on one's needs there are cases where equal and not equal are not opposite; however, the vast majority of cases they are opposite, so in Python 3 if you do not specify a __ne__ method Python will invert the __eq__ method for you.
If you are writing code to run on both Python 2 and Python 3, then you should define both.
Per the data model documentation, which covers the "magic methods" you can implement on classes (emphasis mine):
There are no implied relationships among the comparison operators. The
truth of x==y does not imply that x!=y is false. Accordingly, when
defining __eq__(), one should also define __ne__() so that the
operators will behave as expected.
Seems you are returning True instead of doing the comparison.

Categories