Python 3中的可比较类

38 投票
6 回答
25301 浏览
提问于 2025-04-16 22:42

在Python 3中,怎样才能让一个类可以进行比较呢?比如说,可以通过ID来比较。

6 个回答

1

从Python 3.7开始,你可以使用@dataclass这个功能:

from dataclasses import dataclass
@dataclass(order=True)
class MyObject(object):

    def __init__(self, *, name=None):
        self.name = name
15

为了拥有一整套的比较功能,我使用了下面这个混合类(mixin),你可以把它放在你的模块里的一个文件,比如叫 mixin.py。

class ComparableMixin(object):
    def _compare(self, other, method):
        try:
            return method(self._cmpkey(), other._cmpkey())
        except (AttributeError, TypeError):
            # _cmpkey not implemented, or return different type,
            # so I can't compare with "other".
            return NotImplemented

    def __lt__(self, other):
        return self._compare(other, lambda s, o: s < o)

    def __le__(self, other):
        return self._compare(other, lambda s, o: s <= o)

    def __eq__(self, other):
        return self._compare(other, lambda s, o: s == o)

    def __ge__(self, other):
        return self._compare(other, lambda s, o: s >= o)

    def __gt__(self, other):
        return self._compare(other, lambda s, o: s > o)

    def __ne__(self, other):
        return self._compare(other, lambda s, o: s != o)

要使用上面的混合类,你需要实现一个叫 _cmpkey() 的方法,这个方法会返回一个可以进行比较的对象的关键字,类似于排序时用的 key() 函数。实现的方式可以是这样的:

>>> from .mixin import ComparableMixin

>>> class Orderable(ComparableMixin):
...
...     def __init__(self, firstname, lastname):
...         self.first = firstname
...         self.last = lastname
...
...     def _cmpkey(self):
...         return (self.last, self.first)
...
...     def __repr__(self):
...         return "%s %s" % (self.first, self.last)
...
>>> sorted([Orderable('Donald', 'Duck'), 
...         Orderable('Paul', 'Anka')])
[Paul Anka, Donald Duck]

我之所以使用这个方法而不是 total_ordering 的做法,是因为这个bug。这个问题在 Python 3.4 中已经修复,但有时候你还需要支持旧版本的 Python。

48

要让类之间可以比较,你只需要实现一个叫做 __lt__ 的方法,并且用 functools.total_ordering 来装饰这个类。如果可以的话,你还应该提供一个 __eq__ 方法。这样做可以自动提供其他比较操作符,这样你就不需要自己去写它们了。

撰写回答