В Python 2.x есть два способа перегрузки операторов сравнения __cmp__
или «многофункциональных операторов сравнения», таких как __lt__
. Говорят, что предпочтительнее использовать богатые перегрузки для сравнения, но почему это так?
Каждый из расширенных операторов сравнения проще реализовать, но вы должны реализовать несколько из них с почти идентичной логикой. Однако, если вы можете использовать встроенные функции cmp
и порядок кортежей, это __cmp__
будет довольно просто и выполнит все сравнения:
class A(object):
def __init__(self, name, age, other):
self.name = name
self.age = age
self.other = other
def __cmp__(self, other):
assert isinstance(other, A) # assumption for this example
return cmp((self.name, self.age, self.other),
(other.name, other.age, other.other))
Эта простота, кажется, удовлетворяет мои потребности намного лучше, чем перегрузка всех 6 (!) Богатых сравнений. (Тем не менее, вы можете снизить его до «всего» 4, если будете полагаться на «замененный аргумент» / отраженное поведение, но это, по моему скромному мнению, приводит к чистому увеличению сложности.)
Есть ли какие-то непредвиденные ловушки, о которых мне нужно знать, если я только перегружаюсь __cmp__
?
Я понимаю <
, <=
, ==
и т.д. операторы могут быть перегружены для других целей, и может вернуть любой объект , им нравится. Я не спрашиваю о достоинствах этого подхода, а только о различиях при использовании этих операторов для сравнения в том же смысле, что и для чисел.
Обновление: как указал Кристофер , cmp
исчезает в 3.x. Существуют ли альтернативы, которые упрощают выполнение сравнений, как указано выше __cmp__
?
источник
Ответы:
Да, легко реализовать все в терминах, например,
__lt__
класса миксина (или метакласса, или декоратора класса, если вам так нравится).Например:
Теперь ваш класс может определять просто
__lt__
и умножать наследование от ComparableMixin (после любых других баз, которые ему нужны, если они есть). Декоратор классов был бы очень похож, просто вставляя аналогичные функции в качестве атрибутов нового класса, который он украшает (результат может быть микроскопически быстрее во время выполнения при столь же незначительных затратах с точки зрения памяти).Конечно, если у вашего класса есть какой-то особенно быстрый способ реализации (например),
__eq__
и__ne__
он должен определять их напрямую, чтобы версии миксина не использовались (например, это такdict
) - на самом деле,__ne__
вполне можно определить, чтобы облегчить это как:но в приведенном выше коде я хотел сохранить приятную симметрию только использования
<
;-). Что касается того, почему__cmp__
нужно было уйти, если у нас были__lt__
друзья, зачем использовать другой, другой способ делать то же самое? Это просто мертвый груз в каждой среде выполнения Python (Classic, Jython, IronPython, PyPy, ...). Код, в котором определенно не будет ошибок, - это код, которого нет. Отсюда принцип Python, согласно которому в идеале должен быть один очевидный способ выполнения задачи (в C тот же принцип в разделе «Дух C» стандарт ISO, кстати).Это вовсе не означает , что мы выходим из нашего пути , чтобы запретить вещи (например, вблизи эквивалентность между Mixins и классом декораторами для некоторых применений), но это , безусловно , делает означает , что мы не нравится носить с собой код компиляторов и / или среды выполнения, которые существуют избыточно только для поддержки нескольких эквивалентных подходов для выполнения одной и той же задачи.
Дальнейшее редактирование: на самом деле есть еще лучший способ обеспечить сравнение и хеширование для многих классов, включая тот
__key__
, который указан в вопросе - метод, как я упоминал в своем комментарии к вопросу. Поскольку я так и не успел написать для него PEP, вы должны реализовать его с помощью Mixin (& c), если он вам нравится:Очень часто сравнение экземпляра с другими экземплярами сводится к сравнению кортежа для каждого с несколькими полями, а затем хеширование должно быть реализовано на той же основе. Этот
__key__
специальный метод обращается напрямую к тому, что нужно.источник
TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixin
когда я пробую это на Python 3. Смотрите полный код на gist.github.com/2696496functools.total_ordering
вместо создания своего собственногоComparableMixim
. Как было предложено в ответе jmagnusson<
для реализации__eq__
в Python 3 - довольно плохая идея из-заTypeError: unorderable types
.Чтобы упростить этот случай, в Python 2.7 + / 3.2 + есть декоратор классов, functools.total_ordering , который можно использовать для реализации того, что предлагает Алекс. Пример из документов:
источник
total_ordering
не реализует__ne__
, так что будьте осторожны!__ne__
. но это потому, что__ne__
есть реализация по умолчанию, которая делегирует__eq__
. Так что здесь не на что смотреть.Это описано в PEP 207 - Rich Comparisons
Кроме того,
__cmp__
уходит в python 3.0. (Обратите внимание, что его нет на http://docs.python.org/3.0/reference/datamodel.html, но он ЕСТЬ на http://docs.python.org/2.7/reference/datamodel.html )источник
(Отредактировано 17.06.17, чтобы учесть комментарии.)
Я попробовал сопоставимый ответ на миксин выше. У меня были проблемы с "None". Вот модифицированная версия, которая обрабатывает сравнения на равенство с «Нет». (Я не видел причин беспокоиться о сравнении неравенства с None как с отсутствующей семантикой):
источник
self
может быть синглтонNone
изNoneType
и в то же время реализовать вашиComparableMixin
? И действительно, этот рецепт плох для Python 3.self
не будет никогда бытьNone
, так что отрасль может пойти полностью. Не используйтеtype(other) == type(None)
; просто используйтеother is None
. Вместо специального корпусаNone
, испытание , если другой тип является экземпляром типаself
, и возвращаетNotImplemented
синглтон , если нет:if not isinstance(other, type(self)): return NotImplemented
. Сделайте это для всех методов. Затем Python может дать другому операнду возможность дать ответ.Вдохновленный ответами Алекса Мартелли
ComparableMixin
иKeyedMixin
ответами, я придумал следующий миксин. Он позволяет вам реализовать единственный_compare_to()
метод, который использует сравнения на основе ключей, аналогичныйKeyedMixin
, но позволяет вашему классу выбрать наиболее эффективный ключ сравнения в зависимости от типаother
. (Обратите внимание, что этот миксин не очень помогает для объектов, которые можно проверить на равенство, но не на порядок).источник