问题
Let's say I have a class, members of which I want to compare with the usual operators ==
, <
, <=
, >
, and >=
.
As I understand, this could be accomplished by initializing defining magic method __cmp__(a, b)
which returns -1 (a < b
), 0 (a == b
), or 1 (a > b
).
It seems like __cmp__
was deprecated since Python 3 in favor of defining __eq__
, __lt__
, __le__
, __gt__
, and _ge__
methods separately.
I defined __eq__
and __lt__
assuming that the default values for __le__
would look something like return a == b or a < b
. It seems to be not the case with the following class:
class PQItem:
def __init__(self, priority, item):
self.priority = priority
self.item = item
def __eq__(self, other):
return isinstance(other, PQItem) and self.priority == other.priority
def __lt__(self, other):
return isinstance(other, PQItem) and self.priority < other.priority
class NotComparable:
pass
x = NotComparable()
y = NotComparable()
# x < y gives error
And I get this outcome:
>>> PQItem(1, x) == PQItem(1, y)
True
>>> PQItem(1, x) < PQItem(1, y)
False
>>> PQItem(1, x) <= PQItem(1, y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: PQItem() <= PQItem()
This has left me to conclude that I'll have to define all the comparison magic methods manually to make a class comparable. Is there any better way?
Why has __cmp__
been deprecated? That seems like a much nicer way to deal with it
回答1:
For two objects a
and b
, __cmp__
requires that one of a < b
, a == b
, and a > b
is true. But that might not be the case: consider sets, where it's very common that none of those are true, e.g. {1, 2, 3}
vs {4, 5, 6}
.
So __lt__
and the likes were introduced. But that left Python with two separate ordering mechanisms, which is kind of ridiculous, so the less flexible one was removed in Python 3.
You don't actually have to implement all six comparison methods. You can use the functools.total_ordering class decorator to help define the rest of the magic comparison methods:
from functools import total_ordering
@total_ordering
class PQItem:
def __init__(self, priority, item):
self.priority = priority
self.item = item
def __eq__(self, other):
return isinstance(other, PQItem) and self.priority == other.priority
def __lt__(self, other):
return isinstance(other, PQItem) and self.priority < other.priority
来源:https://stackoverflow.com/questions/52027891/to-make-objects-of-a-custom-class-comparable-is-it-enough-to-define-just-a-few