To make objects of a custom class comparable, is it enough to define just a few of the members in `__eq__` and `__lt__` family?

北慕城南 提交于 2020-01-15 05:25:07

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!