在 Python 3 中使类具有可比性的标准方法是什么?(例如,通过 id。)
5 回答
为了使类具有可比性,您只需要__lt__
使用functools.total_ordering
. 如果可能,您还应该提供一种__eq__
方法。这提供了其余的比较运算符,因此您不必自己编写它们中的任何一个。
对于一整套比较函数,我使用了以下 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)
要使用上面的 mixin,您需要实现一个 _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 配方的原因是这个错误。它在 Python 3.4 中已修复,但通常您还需要支持较旧的 Python 版本。
你说你正在尝试这样做:
max((f(obj), obj) for obj in obj_list)[1]
你应该这样做:
max(f(obj) for obj in obj_list)
编辑:或者如 gnibbler 所说: max(obj_list, key=f)
但是你告诉 gnibbler 你需要一个对 max 对象的引用。我认为这是最简单的:
def max_obj(obj_list, max_fn):
if not obj_list:
return None
obj_max = obj_list[0]
f_max = max_fn(obj)
for obj in obj_list[1:]:
if max_fn(obj) > f_max:
obj_max = obj
return obj_max
obj = max_obj(obj_list)
当然,如果您尝试查找空列表的 max_obj(),您可能希望让它引发异常而不是返回 none。
我只是想到了一种非常骇人听闻的方法。这与您最初尝试做的精神相同。它不需要向类对象添加任何函数;它适用于任何课程。
max(((f(obj), obj) for obj in obj_list), key=lambda x: x[0])[1]
我真的不喜欢这样,所以这里有一些不太简洁的东西可以做同样的事情:
def make_pair(f, obj):
return (f(obj), obj)
def gen_pairs(f, obj_list):
return (make_pair(f, obj) for obj in obj_list)
def item0(tup):
return tup[0]
def max_obj(f, obj_list):
pair = max(gen_pairs(f, obj_list), key=item0)
return pair[1]
obj_list
或者,如果总是像列表这样的可索引对象,则可以使用此单行:
obj_list[max((f(obj), i) for i, obj in enumerate(obj_list))[1]]
这样做的好处是,如果有多个对象f(obj)
返回相同的值,您就知道将获得哪一个:具有最高索引的对象,即列表中最新的对象。如果你想要列表中最早的那个,你可以用一个键函数来做到这一点。