Почему Python не предоставляет реализации по умолчанию __le__ и __ge__? - PullRequest
0 голосов
/ 05 марта 2020

Математически бинарное отношение ≤ является объединением бинарных отношений <и =, в то время как бинарное отношение ≥ является объединением бинарных отношений> и =. Так есть ли причина, по которой Python не реализует __le__ в терминах __lt__ и __eq__ по умолчанию и __ge__ в терминах __gt__ и __eq__ по умолчанию?

Реализации по умолчанию могут быть такими (но, вероятно, в C для производительности, например __ne__):

def __le__(self, other):
    result_1 = self.__lt__(other)
    result_2 = self.__eq__(other)
    if result_1 is not NotImplemented and result_2 is not NotImplemented:
        return result_1 or result_2
    return NotImplemented

def __ge__(self, other):
    result_1 = self.__gt__(other)
    result_2 = self.__eq__(other)
    if result_1 is not NotImplemented and result_2 is not NotImplemented:
        return result_1 or result_2
    return NotImplemented

Это избавит пользователей от реализации этих двух методов все время.

Вот соответствующий параграф в Python документации (выделено мной):

По умолчанию __ne__() делегирует __eq__() и инвертирует результат, если только он это NotImplemented. Других подразумеваемых отношений между операторами сравнения нет, например, , правда (x<y or x==y) не подразумевает x<=y.

Примечание. - Следующие отношения всегда действительны и поэтому применяются по умолчанию в Python (, за исключением отношений объединения, которые кажутся произвольными и являются причиной этого сообщения ):

  • 2 дополнительные отношения: «= и ≠ являются дополнением друг друга»;
  • 6 обратные отношения *: «= является обратным для себя», «≠ является обратное само по себе "," <и> - обратное значение друг друга ", а" ≤ и ≥ - обратное значение друг друга ";
  • 2 union взаимосвязи:" ≤ is union и = ".

Следующие отношения действительны только для итоговых заказов и, следовательно, не применяются по умолчанию в Python (но пользователи может удобно реализовать их, когда они действительны с functools.total_ordering декоратор класса, предоставляемый стандартной библиотекой Python):

  • 4 дополнительные отношения: «<и ≥ являются дополнением друг друга» и «> и ≤ являются дополнением друг друга».

* Обратные связи реализованы в Python через NotImplemented протокол .

Ответы [ 2 ]

3 голосов
/ 05 марта 2020

Почему именно это решение было принято, знает только автор оригинала, но, учитывая эти подсказки из соображений руководства, можно сделать вывод:

Чтобы автоматически генерировать операции заказа из одной операции root, см. functools.total_ordering().

Хотя этот декоратор позволяет легко создавать хорошо упорядоченные полностью упорядоченные типы, он делает за счет более медленных выполнение и более сложные трассировки стека для производных методов сравнения. Если сравнительный анализ производительности показывает, что это является узким местом для данного приложения, реализация всех шести богатых методов сравнения, скорее всего, обеспечит легкое повышение скорости.

Сопоставьте это с мантрой Python явный лучше, чем неявный , следующие рассуждения должны быть удовлетворительными:

Получение __ne__ из __eq__ практически свободно, это просто операция not o.__eq__(other), то есть инвертирование логического значения.

Однако получение __le__ из объединения __lt__ и __eq__ означает, что необходимо вызвать оба метода, что может привести к потенциально значительному снижению производительности, если сравнение будет достаточно сложным, особенно для по сравнению с оптимизированной единственной реализацией __le__. Python позволяет явно включить это удобство и производительность с помощью декоратора total_ordering, но это не будет косвенно накладывать на вас.

Вы также можете выступать за явные ошибки, если пытаетесь выполнять неосуществленные сравнения вместо неявного получения d Сравнения, которые вы не реализовали и которые могут создавать незначительные ошибки, в зависимости от того, что вы имели в виду , чтобы сделать с вашими пользовательскими классами. Python здесь не будет давать никаких предположений, а вместо этого предоставит вам возможность либо явным образом реализовать желаемые сравнения, либо снова явным образом отказаться от производных сравнений.

2 голосов
/ 05 марта 2020

TLDR: операторы сравнения не обязаны возвращать bool. Это означает, что результаты могут не строго соответствовать "a <= b is a < b or a == b" или подобным отношениям. Наиболее важно, что логическое значение or может не сохранить свою семантику.

Автоматическое создание специальных методов может молча привести к неправильному поведению, аналогично тому, как automati c __bool__ обычно не применяется . (В этом примере также <= et c. Больше, чем bool.)


Примером является выражение временных точек через операторы сравнения. Например, среда моделирования usim (заявление об отказе: я поддерживаю этот пакет) определяет моменты времени, которые можно проверить и ожидали . Мы можем использовать сравнения для описания «в или после» некоторого момента времени:

  • time > 2000 после 2000 г.
  • time == 2000 в 2000 г.
  • time >= 2000 до или после 2000 года.

(То же самое относится к < и ==, но ограничение объяснить сложнее.)

Примечательно, что есть два особенности каждого выражения: удовлетворено ли оно прямо сейчас (bool(time >= 2000)) и , когда оно будет удовлетворено (await (time >= 2000)). Первое, очевидно, может быть оценено для каждого случая. Однако вторая не может.

Ожидание == и >= может быть сделано путем ожидания / сна до точного момента времени. Однако ожидание > требует ожидания момента времени плюс некоторая бесконечно небольшая задержка . Последнее не может быть точно выражено, так как для современных типов чисел не существует обобщенных c бесконечно малых, но ненулевых чисел.

Таким образом, результат == и >= в основном другой вид, чем >. Вывести >= как "> or ==" было бы неправильно. Таким образом, usim.time определяет == и >=, но не >, чтобы избежать ошибок. Автоматическое определение операторов сравнения предотвратит это или неправильно определит операторы.

...