I have a class Quantity and I want to implement the __lt__ operator to overload. I have implemented the method as shown below.
def __lt__(self, other):
if isinstance(other, Quantity):
return other.value < self.value
if isinstance(other, numbers.Real):
return self.value < other
raise NotImplemented
When I import it into the python console and try to execute it
>>> Quantity(1.2) > 1
I get the following error
AttributeError: 'int' object has no attribute 'number'
And if I try to execute it like this
>>> Quantity(1.2) < 1
I get the following error
TypeError: '<' not supported between instances of 'Quantity' and 'int'
Does anyone know how can I implement the overload to work with < and >?
Update
Here is the full class
class Quantity:
def __init__(self, value):
self.value = float(value)
def __float__(self):
return self.value
def __repr__(self):
return 'Quantity({})'.format(self.value)
def __str__(self):
return str(self.value)
def __lt__(self, other):
if isinstance(other, Quantity):
return other.value < self.value
if isinstance(other, numbers.Real):
return self.value > other
raise NotImplemented
#property
def value(self):
return self._value
#value.setter
def value(self, value):
value = round(value, 10)
self._value = int(value * 1000) / 1000.0
You get an error on Quantity(1.2) > 1 because you have not defined __gt__ for your Quantity class. As pointed out, you also have an error in your __lt__ method and perform the comparison the wrong way around (other.value < self.value rather than self.value < other.value). I believe this should work:
def __lt__(self, other):
if isinstance(other, Quantity):
return self.value < other.value
if isinstance(other, numbers.Real):
return self.value < other
raise NotImplemented
def __gt__(self, other):
if isinstance(other, Quantity):
return self.value > other.value
if isinstance(other, numbers.Real):
return self.value > other
raise NotImplemented
Your second example, Quantity(1.2) < 1 should work. The error message you quote on that one is not reproducible, it seems (I checked python 2.7 and 3.8).
Related
I have following class:
class Word:
def __init__(self, key: str):
self.key = key
self.value = ''.join(sorted(key))
def __lt__(self, other):
if self.value < other.value:
return True
return False
def __gt__(self, other):
if self.value > other.value:
return True
return False
def __eq__(self, other):
val = other.value
if self.value == val:
return True
return False
and < works. But when I try <= I get following error:
TypeError: '<=' not supported between instances of 'Word' and 'Word'
How to override <= for python class?
You need to implement __ge__ & __le__
class Word:
def __init__(self, key: str):
self.key = key
self.value = ''.join(sorted(key))
def __lt__(self, other):
if self.value < other.value:
return True
return False
def __gt__(self, other):
if self.value > other.value:
return True
return False
def __le__(self, other):
if self.value <= other.value:
return True
return False
def __ge__(self, other):
if self.value >= other.value:
return True
return False
def __eq__(self, other):
val = other.value
if self.value == val:
return True
return False
You need to override def __le__(self, other) (lesser-equal) and def __ge__(self, other) (greater equal) as well.
Beside that you should check if your given other actually is a Word instance, else you might crash because no other.value can be accessed:
w = Word("hello")
print( w > 1234 ) # crash: AttributeError: 'int' object has no attribute 'value'
Source / Documentation of all of them: object.__le__
See
python overloading operators
Potential fix for comparison:
def __lt__(self, other):
if isinstance(other, Word):
if self.value < other.value:
return True
else:
# return True or False if it makes sense - else use better exception
raise ValueError(f"Cannot compare Word vs. {type(other)}")
return False
There is another special method for this:
def __le__(self, other):
if self.value <= other.value:
return True
else:
return False
I have a simple python class, that I want to be able to compare. So I implemented compare operators. I then realized that I've been doing that same thing for so many classes, and it feels a lot like code duplication.
class Foo(object):
def __init__(self, index, data):
self.index = index
self.data = data
def __lt__(self, other):
return self.index < other.index
def __gt__(self, other):
return self.index > other.index
def __le__(self, other):
return self.index <= other.index
def __ge__(self, other):
return self.index >= other.index
def __eq__(self, other):
return self.index == other.index
def __ne__(self, other):
return self.index != other.index
So I think a simple solution would be something like this:
class Comparable(object):
def _compare(self, other):
raise UnimplementedError()
def __lt__(self, other):
return self._compare(other) < 0
def __gt__(self, other):
return self._compare(other) > 0
def __le__(self, other):
return self._compare(other) <= 0
def __ge__(self, other):
return self._compare(other) >= 0
def __eq__(self, other):
return self._compare(other) == 0
def __ne__(self, other):
return self._compare(other) != 0
class Foo1(Comparable):
def _compare(self, other):
return self.index - other.index
class Foo2(Comparable):
def _compare(self, other):
# ...
class Foo3(Comparable):
def _compare(self, other):
# ...
But it seems so basic, that I feel like I'm reinventing the wheel here.
I'm wondering if there a more 'native' way to achieve that.
As described in the docs you can use functools.total_ordering to save some boilerplate in writing all of the comparisons
To avoid the hassle of providing all six functions, you can implement __eq__, __ne__, and only one of the ordering operators, and use the functools.total_ordering() decorator to fill in the rest.
To be explicit, the six functions they are referring to are: __eq__, __ne__, __lt__, __le__, __gt__, and __ge__.
So, you want some automation while creating rich comparison methods. You can have this behaviour by using functools.total_ordering() higher-order function. See the reference for more details.
This is my implementation of a MinHeap and MaxHeap in python. This uses a comparator to reverse the sequence of storage in the MaxHeap
import heapq
class MinHeap:
def __init__(self):
self.heap = []
def push(self, item):
heapq.heappush(self.heap, item)
def pop(self):
return heapq.heappop(self.heap)
def peek(self):
return self.heap[0]
def __getitem__(self, item):
return self.heap[item]
def __len__(self):
return len(self.heap)
class MaxHeap(MinHeap):
def push(self, item):
heapq.heappush(self.heap, Comparator(item))
def pop(self):
return heapq.heappop(self.heap)
def peek(self):
return self.heap[0]
def __getitem__(self, i):
return self.heap[i].val
class Comparator:
def __init__(self, val):
self.val = val
def __lt__(self, other):
return self.val > other
def __eq__(self, other):
return self.val == other
if __name__ == '__main__':
max_heap = MaxHeap()
max_heap.push(12)
max_heap.push(3)
max_heap.push(17)
print(max_heap.pop())
The MinHeap seems to work fine, however the MaxHeap throw up the following error.
<__main__.Comparator object at 0x10a5c1080>
I don't quite seem to understand what am I doing wrong here. Can someone help me with this.
I've added __repr__ and __gt__ methods to your Comparator class, so the code now runs, and the Comparator instances display their val when printed.
The important thing is to get those comparison methods to do the comparisons correctly between two Comparator instances.
You'll notice that I've eliminated most of the methods from MaxHeap. They aren't needed because the methods inherited from MinHeap work ok. You may wish to restore this one to MaxHeap
def __getitem__(self, i):
return self.heap[i].val
depending on how you intend to use MaxHeap.
import heapq
class MinHeap:
def __init__(self):
self.heap = []
def push(self, item):
heapq.heappush(self.heap, item)
def pop(self):
return heapq.heappop(self.heap)
def peek(self):
return self.heap[0]
def __getitem__(self, item):
return self.heap[item]
def __len__(self):
return len(self.heap)
class MaxHeap(MinHeap):
def push(self, item):
heapq.heappush(self.heap, Comparator(item))
class Comparator:
def __init__(self, val):
self.val = val
def __lt__(self, other):
return self.val > other.val
def __eq__(self, other):
return self.val == other.val
def __repr__(self):
return repr(self.val)
if __name__ == '__main__':
max_heap = MaxHeap()
max_heap.push(12)
max_heap.push(3)
max_heap.push(17)
while True:
try:
print(max_heap.pop())
except IndexError:
# The heap's empty, bail out
break
output
17
12
3
It's probably a Good Idea to give Comparator the full set of rich comparison methods. They aren't needed to make the above code work, but they will make the Comparator instances more flexible. So in case you want them, here they are:
def __lt__(self, other):
return self.val > other.val
def __le__(self, other):
return self.val >= other.val
def __gt__(self, other):
return self.val < other.val
def __ge__(self, other):
return self.val <= other.val
def __eq__(self, other):
return self.val == other.val
def __ne__(self, other):
return self.val != other.val
Since Python 3.4, the Enum class exists.
I am writing a program, where some constants have a specific order and I wonder which way is the most pythonic to compare them:
class Information(Enum):
ValueOnly = 0
FirstDerivative = 1
SecondDerivative = 2
Now there is a method, which needs to compare a given information of Information with the different enums:
information = Information.FirstDerivative
print(value)
if information >= Information.FirstDerivative:
print(jacobian)
if information >= Information.SecondDerivative:
print(hessian)
The direct comparison does not work with Enums, so there are three approaches and I wonder which one is preferred:
Approach 1: Use values:
if information.value >= Information.FirstDerivative.value:
...
Approach 2: Use IntEnum:
class Information(IntEnum):
...
Approach 3: Not using Enums at all:
class Information:
ValueOnly = 0
FirstDerivative = 1
SecondDerivative = 2
Each approach works, Approach 1 is a bit more verbose, while Approach 2 uses the not recommended IntEnum-class, while and Approach 3 seems to be the way one did this before Enum was added.
I tend to use Approach 1, but I am not sure.
Thanks for any advise!
You should always implement the rich comparison operaters if you want to use them with an Enum. Using the functools.total_ordering class decorator, you only need to implement an __eq__ method along with a single ordering, e.g. __lt__. Since enum.Enum already implements __eq__ this becomes even easier:
>>> import enum
>>> from functools import total_ordering
>>> #total_ordering
... class Grade(enum.Enum):
... A = 5
... B = 4
... C = 3
... D = 2
... F = 1
... def __lt__(self, other):
... if self.__class__ is other.__class__:
... return self.value < other.value
... return NotImplemented
...
>>> Grade.A >= Grade.B
True
>>> Grade.A >= 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Grade() >= int()
Terrible, horrible, ghastly things can happen with IntEnum. It was mostly included for backwards-compatibility sake, enums used to be implemented by subclassing int. From the docs:
For the vast majority of code, Enum is strongly recommended, since
IntEnum breaks some semantic promises of an enumeration (by being
comparable to integers, and thus by transitivity to other unrelated
enumerations). It should be used only in special cases where there’s
no other choice; for example, when integer constants are replaced with
enumerations and backwards compatibility is required with code that
still expects integers.
Here's an example of why you don't want to do this:
>>> class GradeNum(enum.IntEnum):
... A = 5
... B = 4
... C = 3
... D = 2
... F = 1
...
>>> class Suit(enum.IntEnum):
... spade = 4
... heart = 3
... diamond = 2
... club = 1
...
>>> GradeNum.A >= GradeNum.B
True
>>> GradeNum.A >= 3
True
>>> GradeNum.B == Suit.spade
True
>>>
I hadn'r encountered Enum before so I scanned the doc (https://docs.python.org/3/library/enum.html) ... and found OrderedEnum (section 8.13.13.2) Isn't this what you want? From the doc:
>>> class Grade(OrderedEnum):
... A = 5
... B = 4
... C = 3
... D = 2
... F = 1
...
>>> Grade.C < Grade.A
True
Combining some of the above ideas, you can subclass enum.Enum to make it comparable to string/numbers and then build your enums on this class instead:
import numbers
import enum
class EnumComparable(enum.Enum):
def __gt__(self, other):
try:
return self.value > other.value
except:
pass
try:
if isinstance(other, numbers.Real):
return self.value > other
except:
pass
return NotImplemented
def __lt__(self, other):
try:
return self.value < other.value
except:
pass
try:
if isinstance(other, numbers.Real):
return self.value < other
except:
pass
return NotImplemented
def __ge__(self, other):
try:
return self.value >= other.value
except:
pass
try:
if isinstance(other, numbers.Real):
return self.value >= other
if isinstance(other, str):
return self.name == other
except:
pass
return NotImplemented
def __le__(self, other):
try:
return self.value <= other.value
except:
pass
try:
if isinstance(other, numbers.Real):
return self.value <= other
if isinstance(other, str):
return self.name == other
except:
pass
return NotImplemented
def __eq__(self, other):
if self.__class__ is other.__class__:
return self == other
try:
return self.value == other.value
except:
pass
try:
if isinstance(other, numbers.Real):
return self.value == other
if isinstance(other, str):
return self.name == other
except:
pass
return NotImplemented
You can create a simple decorator to resolve this too:
from enum import Enum
from functools import total_ordering
def enum_ordering(cls):
def __lt__(self, other):
if type(other) == type(self):
return self.value < other.value
raise ValueError("Cannot compare different Enums")
setattr(cls, '__lt__', __lt__)
return total_ordering(cls)
#enum_ordering
class Foos(Enum):
a = 1
b = 3
c = 2
assert Names.a < Names.c
assert Names.c < Names.b
assert Names.a != Foos.a
assert Names.a < Foos.c # Will raise a ValueError
For bonus points you could implement the other methods in #VoteCoffee's answer above
for those who want to use the == with two enum instances like that: enum_instance_1 == enum_instance_2
just add the __eq__ method in your Enum class as follows:
def __eq__(self, other):
return self.__class__ is other.__class__ and other.value == self.value
I have created a new Python object as follows
class Mylist(list):
def __cmp__(self,other):
if len(self)>len(other):
return 1
elif len(self)<len(other):
return -1
elif len(self)==len(other):
return 0
my intend is, when two Mylist objects are compared the object with large number of items should be higher.
c=Mylist([4,5,6])
d=Mylist([1,2,3])
after running the above code, c and d are supposed to be equal(c==d <==True). But I am getting
>>> c==d
False
>>> c>d
True
>>>
they are being compared like the list object itself. What did I do wrong?
You need to implement function __eq__.
class Mylist(list):
def __cmp__(self,other):
if len(self)>len(other):
return 1
elif len(self)<len(other):
return -1
elif len(self)==len(other):
return 0
def __eq__(self, other):
return len(self)==len(other)
UPDATE: (previous code does not work perfectly as explained in comments)
Although #tobias_k answer explains it better, you can do it via __cmp__ function in Python 2 if you insist. You can enable it by removing other compare functions (le,lt,ge, ...):
class Mylist(list):
def __cmp__(self,other):
if len(self)>len(other):
return 1
elif len(self)<len(other):
return -1
elif len(self)==len(other):
return 0
def __eq__(self, other):
return len(self)==len(other)
#property
def __lt__(self, other): raise AttributeError()
#property
def __le__(self, other): raise AttributeError()
#property
def __ne__(self, other): raise AttributeError()
#property
def __gt__(self, other): raise AttributeError()
#property
def __ge__(self, other): raise AttributeError()
The problem seems to be that list implements all of the rich comparison operators, and __cmp__ will only be called if those are not defined. Thus, it seems like you have to overwrite all of those:
class Mylist(list):
def __lt__(self, other): return cmp(self, other) < 0
def __le__(self, other): return cmp(self, other) <= 0
def __eq__(self, other): return cmp(self, other) == 0
def __ne__(self, other): return cmp(self, other) != 0
def __gt__(self, other): return cmp(self, other) > 0
def __ge__(self, other): return cmp(self, other) >= 0
def __cmp__(self, other): return cmp(len(self), len(other))
BTW, it seems like __cmp__ was removed entirely in Python 3. The above works in Python 2.x, but for compatibility you should probably rather do it like
def __lt__(self, other): return len(self) < len(other)
Also see these two related questions. Note that while in Python 3 it would be enough to implement __eq__ and __lt__ and have Python infer the rest, this will not work in this case, since list already implements all of them, so you have to overwrite them all.