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.
Related
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 ==.
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).
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)
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.
I want to implement Fast Marching Method for Inpainting in Python. In literature, this has been implemented using a min-heap. Since it involves adding, removing and reordering the data structure many times, and each time extracting the smallest element. So the complexity for these operations need to be min preferably.
I know there is a heapq in-built module in Python. It accepts a single float value. However, I need to store 3 different information content corresponding to a pixel. Is there a way I can tweak heapq to accept a list perhaps?
Alternatively, is there a different data structure with this functionality?
heapq takes any type, as long as they are orderable. The items must either support the < lower then or the <= lower or equal than operator (heapq will use the latter if the first isn't available).
For example, you could use tuples ((priority, your_data_structure)); tuples have a relative order based on their contents, starting with the first item.
Or you can use custom objects that implement at least one of __lt__, __le__, __gt__ or __ge__ to implement comparisons between them and thus define an ordering (and, preferably, include a __eq__ equality method too). The functools. total_ordering() decorator would then supply your class with the remaining methods:
from functools import total_ordering
#total_ordering
class PixelInfo(object):
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def __eq__(self, other):
if not isinstance(other, type(self)): return NotImplemented
return all(getattr(self, c) == getattr(other, c) for c in 'rgb')
def __lt__(self, other):
if not isinstance(other, type(self)): return NotImplemented
return self.r + self.g + self.b < other.r + other.g + other.b
would be an orderable custom class, which heapq would be happy to handle for you.