Каков стандартный способ сделать класс сопоставимым в Python 3? (Например, по идентификатору.)
Сопоставимые классы в Python 3
Ответы (5)
Чтобы сделать классы сопоставимыми, вам нужно только реализовать __lt__
и украсить класс functools.total_ordering
а>. Вы также должны предоставить метод __eq__
, если это возможно. Это обеспечивает остальные операторы сравнения, поэтому вам не нужно писать ни один из них самостоятельно.
functools.total_ordering
.
- person Neil G; 02.08.2011
__eq__
.
- person Entropic Thunder; 09.05.2021
Для полного набора функций сравнения я использовал следующий миксин, который вы можете поместить, например, в свой модуль, например, в файл 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, заключается в этой ошибке. Это исправлено в Python 3.4, но часто вам нужно поддерживать и более старые версии Python.
mixin
является гипотетическим модулем, который можно определить — его нет в стандартной библиотеке Python.
- person Neil G; 02.08.2011
functools.total_ordering
?
- person Neil G; 21.05.2015
Не уверен, что это завершено, но вы хотели бы определить:
__eq__, __gt__, __ge__, __lt__, __le__
Как сказал agf, мне не хватает:
__ne__
__ne__
, не равно.
- person agf; 02.08.2011
Вы сказали, что пытаетесь сделать это:
max((f(obj), obj) for obj in obj_list)[1]
Вы должны просто сделать это:
max(f(obj) for obj in obj_list)
РЕДАКТИРОВАТЬ: Или, как сказал гнибблер: 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() пустого списка.
max((f(obj), obj) for obj in obj_list)
- person agf; 02.08.2011
[1]
из его ответа, что было достаточно сложно, чтобы обмануть меня в первый раз, когда я его прочитал.
- person steveha; 02.08.2011
Я просто придумал действительно хакерский способ сделать это. Это в том же духе, что вы изначально пытались сделать. Он не требует добавления каких-либо функций к объекту класса; это работает для любого класса.
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)
возвращает идентичное значение, вы знаете, какой из них вы получите: тот, у которого самый высокий индекс, то есть самый последний в списке. Если вам нужен самый ранний из списка, вы можете сделать это с помощью ключевой функции.
id(obj)
в качестве ключа для сортировки. - person Neil G   schedule 02.08.2011max((f(obj), obj) for obj in obj_list)[1]
чтобы получить объект с наивысшим значением в соответствии сf
. Python 3 жалуется, что obj несопоставим. Мне все равно, как это сравнивать. - person Neil G   schedule 02.08.2011max(obj_list, key=f)
вместо этого чудовища - person John La Rooy   schedule 02.08.2011obj
было максимальноf(obj)
. См. stackoverflow.com/questions/5098580/ - person Neil G   schedule 02.08.2011obj
, так иf(obj)
? - person Neil G   schedule 02.08.2011f
снова по результату. Это, вероятно, более эффективно, чем создание всех этих кортежей - person John La Rooy   schedule 02.08.2011