A person asked a question on SO about how to get a list unique function in python with an alternative equality function.
I was thinking it could be done by inheriting from the element class and overloading the equality function
import functools
#functools.total_ordering
class ffloat(float):
def __eq__(self,other):
if floor(self) == floor(other):
return True
else:
return False
def __le__(self,other):
if self == other:
return True
else:
return float(self) <= float(other)
def __hash__(self):
return floor(self)
a = map(ffloat,[4.3,8,8.9, 13])
In [41]: a[1] == a[2]
Out[41]: True
but
In [42]: set(a)
Out[42]: set([4.3, 8.0, 8.9, 13.0])
Edit: replaced abs < 1.5 equality with floor equality
Added Hash
P.S. is there a way to make a class factory out of this that a class and two lambda and returns a class that inherits from the first one overriding the needed equality function.
This is not a valid equality function, since it's not transitive:
mfloat(0) == mfloat(1) == mfloat(2), but mfloat(0) != mfloat(2).
Also note that in order to be used in a set, you must override __hash__ so that the following property holds for all instances a, b of your class:
a == b ⇒ hash(a) == hash(b)
set finds out that hash(mfloat(8)) != hash(mfloat(9)). Since set assumes the above property holds, it concludes that mfloat(8) != mfloat(9) without actually calling __eq__.
In summary, this works:
from math import floor
class ffloat(float):
def __eq__(self,other):
return floor(self) == floor(other):
def __hash__(self):
return floor(self)
a = map(ffloat,[4.3,8,8.9, 13])
print(set(a))
# output: {8.0, 4.3, 13.0}
Related
Since Python does not provide left/right versions of its comparison operators, how does it decide which function to call?
class A(object):
def __eq__(self, other):
print "A __eq__ called"
return self.value == other
class B(object):
def __eq__(self, other):
print "B __eq__ called"
return self.value == other
>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False
This seems to call both __eq__ functions.
I am looking for the official decision tree.
The a == b expression invokes A.__eq__, since it exists. Its code includes self.value == other. Since int's don't know how to compare themselves to B's, Python tries invoking B.__eq__ to see if it knows how to compare itself to an int.
If you amend your code to show what values are being compared:
class A(object):
def __eq__(self, other):
print("A __eq__ called: %r == %r ?" % (self, other))
return self.value == other
class B(object):
def __eq__(self, other):
print("B __eq__ called: %r == %r ?" % (self, other))
return self.value == other
a = A()
a.value = 3
b = B()
b.value = 4
a == b
it will print:
A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?
When Python2.x sees a == b, it tries the following.
If type(b) is a new-style class, and type(b) is a subclass of type(a), and type(b) has overridden __eq__, then the result is b.__eq__(a).
If type(a) has overridden __eq__ (that is, type(a).__eq__ isn't object.__eq__), then the result is a.__eq__(b).
If type(b) has overridden __eq__, then the result is b.__eq__(a).
If none of the above are the case, Python repeats the process looking for __cmp__. If it exists, the objects are equal iff it returns zero.
As a final fallback, Python calls object.__eq__(a, b), which is True iff a and b are the same object.
If any of the special methods return NotImplemented, Python acts as though the method didn't exist.
Note that last step carefully: if neither a nor b overloads ==, then a == b is the same as a is b.
From https://eev.ee/blog/2012/03/24/python-faq-equality/
Python 3 Changes/Updates for this algorithm
How is __eq__ handled in Python and in what order?
a == b
It is generally understood, but not always the case, that a == b invokes a.__eq__(b), or type(a).__eq__(a, b).
Explicitly, the order of evaluation is:
if b's type is a strict subclass (not the same type) of a's type and has an __eq__, call it and return the value if the comparison is implemented,
else, if a has __eq__, call it and return it if the comparison is implemented,
else, see if we didn't call b's __eq__ and it has it, then call and return it if the comparison is implemented,
else, finally, do the comparison for identity, the same comparison as is.
We know if a comparison isn't implemented if the method returns NotImplemented.
(In Python 2, there was a __cmp__ method that was looked for, but it was deprecated and removed in Python 3.)
Let's test the first check's behavior for ourselves by letting B subclass A, which shows that the accepted answer is wrong on this count:
class A:
value = 3
def __eq__(self, other):
print('A __eq__ called')
return self.value == other.value
class B(A):
value = 4
def __eq__(self, other):
print('B __eq__ called')
return self.value == other.value
a, b = A(), B()
a == b
which only prints B __eq__ called before returning False.
Note that I also correct a small error in the question where self.value is compared to other instead of other.value - in this comparison, we get two objects (self and other), usually of the same type since we are doing no type-checking here (but they can be of different types), and we need to know if they are equal. Our measure of whether or not they are equal is to check the value attribute, which must be done on both objects.
How do we know this full algorithm?
The other answers here seem incomplete and out of date, so I'm going to update the information and show you how how you could look this up for yourself.
This is handled at the C level.
We need to look at two different bits of code here - the default __eq__ for objects of class object, and the code that looks up and calls the __eq__ method regardless of whether it uses the default __eq__ or a custom one.
Default __eq__
Looking __eq__ up in the relevant C api docs shows us that __eq__ is handled by tp_richcompare - which in the "object" type definition in cpython/Objects/typeobject.c is defined in object_richcompare for case Py_EQ:.
case Py_EQ:
/* Return NotImplemented instead of False, so if two
objects are compared, both get a chance at the
comparison. See issue #1393. */
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
So here, if self == other we return True, else we return the NotImplemented object. This is the default behavior for any subclass of object that does not implement its own __eq__ method.
How __eq__ gets called
Then we find the C API docs, the PyObject_RichCompare function, which calls do_richcompare.
Then we see that the tp_richcompare function, created for the "object" C definition is called by do_richcompare, so let's look at that a little more closely.
The first check in this function is for the conditions the objects being compared:
are not the same type, but
the second's type is a subclass of the first's type, and
the second's type has an __eq__ method,
then call the other's method with the arguments swapped, returning the value if implemented. If that method isn't implemented, we continue...
if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
(f = Py_TYPE(w)->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Next we see if we can lookup the __eq__ method from the first type and call it.
As long as the result is not NotImplemented, that is, it is implemented, we return it.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Else if we didn't try the other type's method and it's there, we then try it, and if the comparison is implemented, we return it.
if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
Finally, we get a fallback in case it isn't implemented for either one's type.
The fallback checks for the identity of the object, that is, whether it is the same object at the same place in memory - this is the same check as for self is other:
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
res = (v == w) ? Py_True : Py_False;
break;
Conclusion
In a comparison, we respect the subclass implementation of comparison first.
Then we attempt the comparison with the first object's implementation, then with the second's if it wasn't called.
Finally we use a test for identity for comparison for equality.
Why does my equality method produce True when the 2 objects point and b point to 2 different objects in memory?
import math
def main():
point = Point(2, 3)
print(point == Point(2, 3))
b = Point(2, 3)
print(id(point), id(b))
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def distance_from_origin(self):
return math.hypot(self.x, self.y)
def __eq__(self, other):
return id(self.x) == id(other.x) and id(self.y) == id(other.y)
def __repr__(self):
return f"Point({self.x!r}, {self.y!r})"
def __str__(self):
return f"{self.x!r}, {self.y!r}"
if name == 'main':
main()
id of Point objects are different, because they're different objects and there is no cache/interning mechanism for them (which would be wrong because they're mutable).
== works because when invoking == on Point, you call __eq__ and it's coded like this:
def __eq__(self, other):
return id(self.x) == id(other.x) and id(self.y) == id(other.y)
so, it's wrong, but it works most of the time because of interning of integers from -5 to 256 in CPython (further tests show that it works with bigger values but it's not guaranteed). Counter example:
a = 912
b = 2345
point = Point(a, b)
print(point == Point(456*2, b))
you'll get False even if 456*2 == 912
Rewrite as this so you won't have surprises with big integers:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
If you remove this __eq__ method, you'll get False, as in that case, Python default == operator on an unknown object only has object identity to perform comparisons.
But the purpose of == is to compare object contents, not ids. Coding an equality method that tests identities can lead to surprises, as shown above.
In Python when people use ==, they expect objects to be equal if values are equal. Identities are an implementation detail, just forget about it.
(Previous versions of Python require you do define __ne__ as well, as it's not automatically the inverse of __eq__ and can lead to strange bugs)
In a nutshell: don't use is (besides is None idiom) or id unless you're writing a very complex low-level program with caching and weird stuff or when debugging your program.
Python caches small integers (in range [-5, 256]), thus id(self.x) == id(other.x) and id(self.y) == id(other.y) is True. Since self.x and other.x are the same objects in memory. Find a different way of comparing those two objects or get rid of your custom __eq__ and use the default way (Python will return False for point == Point(2, 3) in that case).
See this answer for more on the issue.
The following code works with Python 2.7:
>>> class Derived(int):
... def __eq__(self, other):
... return int.__eq__(other)
...
>>> Derived(12) == 12.0
True
>>> Derived(12) == 13
False
I do not understand, why it works, given that the self attribute is not explicitly given to int.__eq__() method call.
[EDIT]
Answers so far suggested, that it is about returning NotImplemented by self.__eq__(other) and thus calling other.__eq__(self). Then Derived(12) == Derived(12) I expect to be an infinitive recursion, which is not the case:
>>> Derived(12) == Derived(12)
True
It works because int.__eq__(<something>) returns NotImplemented and when that happens it results in a call to other.__eq__(self) and that's what is returning True and False here.
Demo:
class Derived(int):
def __eq__(self, other):
print self, other
print int.__eq__(other)
print other.__eq__(self)
return int.__eq__(other)
>>> Derived(12) == 12.0
12 12.0
NotImplemented
True
True
>>> Derived(12) == 13.0
12 13.0
NotImplemented
False
False
From NotImplemented
's docs:
Special value which should be returned by the binary special methods
(e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to
indicate that the operation is not implemented with respect to the
other type; may be returned by the in-place binary special methods
(e.g. __imul__(), __iand__(), etc.) for the same purpose. Its
truth value is true.
Note When NotImplemented is returned, the interpreter will then try
the reflected operation on the other type, or some other fallback,
depending on the operator. If all attempted operations return
NotImplemented, the interpreter will raise an appropriate exception.
What happens when both __eq__ return NotImplemented?
The behaviour is different in Python 2 and 3.
In Python 2 it falls back to __cmp__ method first and integers have __cmp__ method in Python 2. It has been removed in Python 3.
As per Python 2 docs if nothing is found it ultimately falls back to identity comparison:
If no __cmp__(), __eq__() or __ne__() operation is defined, class
instances are compared by object identity (“address”)
class Derived(int):
def __eq__(self, other):
print ("Inside __eq__")
return NotImplemented
def __cmp__(self, other):
print ("Inside __cmp__ finally")
return True
>>> Derived(12) == Derived(12)
Inside __eq__
Inside __eq__
Inside __cmp__ finally
False
Not let's define a class with no method defined:
class Derived(object):
pass
>>> Derived() == Derived()
False
>>> d = Derived()
>>> d == d # Same objects.
True
Python 3 doesn't have __cmp__ method anymore but it seems to be falling back to identity now. And it seems it is not documented either.
# Python 3.5
>>> Derived() == Derived()
False
>>> d = Derived()
>>> d == d
True
When mixing float with an integer type, there's no good uniform approach.
https://github.com/python/cpython/blob/2.7/Objects/floatobject.c#L401-L417
P.S.
How int() object using "==" operator without __eq__() method in python2?
In Python 2.7, if you call int.__eq__ it always returns NotImplemented. Example:
>>> int.__eq__(12.0)
NotImplemented
When you use the == operator it will attempt to run the __eq__ method on the left argument, and if it gets NotImplemented it will return the result of the __eq__ method from the argument on the right.
In your example for Derived(12) == 12.0, the interpreter first tries Derived(12).__eq__(12.0), and gets NotImplemented. It then runs the __eq__ method on the float number 12.0 and gets True.
In the case of your Derived(12) == Derived(12) example, what's likely happening is that since both objects return NotImplemented for their __eq__ methods, and since Derived inherits from int, the interpreter falls back to using the cmp builtin behavior for int (according to this answer, which is linked-to in another answer to your question).
Here's an example that illustrates your case:
class Derived(int):
def __eq__(self, other):
print 'Doing eq'
return NotImplemented
def __cmp__(self, other):
print 'doing cmp'
return 0 # contrived example - don't do this
>>> Derived(12) == Derived(12)
doing eq
doing eq
doing cmp
True
Let's say I made an integer wrapper class in Python, like Java has. Most of the methods would be pretty trivial. But the __ eq __ overload (the equality test) poses a fun puzzle: consider the following method
def __eq__( self, other ):
return self.value == int(other)
Implementation details:
The Integer Wrapper has one field, "value," which is an integer
The method __ trunc __ returns the field "value" so that int( Integer(x) ) = x
The constructor for Integer truncates "value" to an integer; Integer(3.1)=Integer(3)
Rules for the Method
Integer(x) == Integer(x) must return true for all integers x
Integer(x) == x must return true for all integers x
Integer(x) == Integer(y) must return false for all inegers (x,y) such that x != y
Integer(x) == y must return false for all x != y
My beautiful method can be susceptible to the very last test. Consider
Integer(1) == 1.1
Will return true.
How can we implement an Integer class under the stated constraints - something that seems trivial, with the stated fairly straight-forward definition of equality?
Note: you might find it bothersome that I claim Integer(1.1) == Integer(1) is a valid result. I'll admit it has some silliness to it, but I have control over how the constructor handles non-integer parameters; I could throw an exception if I wanted claiming unsafe cast. I don't have any control over the fourth case, in which someone asks if my integer equals a primitive of the same value.
Edit
Per request, here's enough code for the class that I think the conditions I've set forth are satisfied:
class Integer:
""" An integer wrapper class. Provides support for using integers as
objects. There are subtelties and pitfalls, however. """
def __init__( self, val = 0 ):
""" Constructs a new integer with a certain value """
self.val = int(val)
def __trunc__( self ):
""" Truncates the internal value """
return int(self.val)
def __eq__( self, other ):
""" Returns true if the object ahs the same value as this object """
return self.val == int(other)
If I interpreted your requirements correctly, this should do it:
>>> class Integer:
... def __init__(self, val=0):
... self.val = int(val)
... def __eq__(self, other):
... return self.val == other
...
>>> Integer(1) == 1.1
False
>>> Integer(1.2) == Integer(1.3)
True
>>> Integer(4) == Integer(7)
False
>>> Integer(2) == 2
True
>>>
If I understand you correctly the issue here is that the value you compare against might be a float, rather than an int, that, in return, would get truncated to an equal int, if compared against.
If that is the case how about checking, if the compared value has a remainder when divided by the comparing value and reacting upon that:
def __eq__( self, other ):
if float(other) % self.value > 0:
return False
else:
return True
That way you can pass in a float that is divisible by self.value(), or the same value as an integer and you return true for all cases where
int(x) == y || int(x) == int(y) || int(x) == float(y), for all x / y = 1
The special work __cmp__ doesn't work. Say the following code:
class Test():
def __cmp__(self, other):
return False
t1 = Test()
t2 = t1
print t2 == t1
I should get False because that the cmp is always returning False. But actually, python is printing True for me.
Any suggestion?
__cmp__ should return -1, 0 or 1, indicating of it's lower than, equal to or higher than other. Returning False will actually make it compare as equal to everything, as the integer value of False is 0.
class Test():
def __cmp__(self, other):
return -1
Also note that __cmp__ is deprecated and is ignored in Python 3. You should implement __eq__ and the other so called rich comparison operators instead.