#python #oop #functools
Вопрос:
Я пытаюсь отсортировать список строк таким образом, чтобы использовать специальное сравнение. Я пытаюсь использовать functools.total_ordering
, но я не уверен, правильно ли он заполняет неопределенные сравнения.
Два, которые я определяю ( > и==), работают так, как ожидалось, но > В частности, я печатаю все три и получаю это a > b
и a < b
. Как это возможно? Я бы подумал, что total_ordering будет просто определять <
как not > and not ==
. Результат моего <
теста-это то, что вы получили бы при регулярном сравнении str, что заставляет меня поверить, что total_ordering ничего не делает.
Возможно, проблема в том, что я наследую str, который уже __lt__
реализован? Если да, то есть ли решение этой проблемы?
from functools import total_ordering
@total_ordering
class SortableStr(str):
def __gt__(self, other):
return self other > other self
#Is this necessary? Or will default to inherited class?
def __eq__(self, other):
return str(self) == str(other)
def main():
a = SortableStr("99")
b = SortableStr("994")
print(a > b)
print(a == b)
print(a < b)
if __name__ == "__main__":
main()
выход:
True
False
True
Комментарии:
1. Вы сказали, что считаете, что «логика[не] очень уместна для моего вопроса». Не могли бы вы все равно поделиться этим? Если вы объясните, какие результаты вы ожидаете увидеть, кто-то, возможно, сможет предложить альтернативное решение. Например, кем вы ожидаете
"555" < "555999"
стать? Или"555" > "555999"
?2. Почему вы сравниваете
next_dig
цифру в той же строке?3. Ах, просто прочитайте свой вопрос немного внимательнее. Вы говорите , что получаете ожидаемые значения
True
дляa > b
иFalse
дляa == b
, но вы получаете неожиданный результатTrue
дляa < b
.4. Я упростил вопрос, мой новый код получает тот же результат. Для контекста я работаю над этой проблемой: leetcode.com/problems/largest-number/solution
5. @PaulFornia Множество встроенных модулей Python, включая сортировку, гарантированно используются только
__lt__
для того , чтобы упростить и сделать более последовательным использование с ним различных классов. См. раздел Сортировка КАК#Всякая всячина . Это также верно в C , где большинство встроенных алгоритмов гарантированно используютсяoperator<
вместо того, чтобы требовать выполнения всех операций упорядочения.
Ответ №1:
Вы правы в том, что встроенные str
операторы сравнения вмешиваются в ваш код. Из документов
Учитывая класс, определяющий один или несколько методов упорядочения богатых сравнений, этот декоратор класса предоставляет все остальное.
Таким образом, он предоставляет только те, которые еще не определены. В вашем случае тот факт, что некоторые из них определены в родительском классе, невозможно обнаружить total_ordering
.
Теперь мы можем углубиться в исходный код и найти точную проверку
roots = {op for op in _convert if getattr(cls, op, None) is not getattr(object, op, None)}
Таким образом, он проверяет, равны ли значения значениям, определенным в корневом объекте object
. Мы можем это сделать
@total_ordering
class SortableStr(str):
__lt__ = object.__lt__
__le__ = object.__le__
__ge__ = object.__ge__
def __gt__(self, other):
return self other > other self
#Is this necessary? Or will default to inherited class?
def __eq__(self, other):
return str(self) == str(other)
Теперь total_ordering
увидим, что __lt__
, __le__
, и __ge__
равны «исходным» object
значениям и перепишем их, по желанию.
Все это, как говорится, я бы сказал, что это плохое использование наследования. Вы нарушаете замену Лискова, по крайней мере, в том, что смешанные сравнения между str
и SortableStr
, мягко говоря, приведут к нелогичным результатам.
Моя более общая рекомендация состоит в том, чтобы отдать предпочтение композиции, а не наследованию, и вместо определения вещи, которая «является» специализированной строкой, рассмотрите возможность определения типа, который «содержит» строку и имеет специализированное поведение.
@total_ordering
class SortableStr:
def __init__(self, value):
self.value = value
def __gt__(self, other):
return self.value other.value > other.value self.value
def __eq__(self, other):
return self.value == other.value
Там не требуется никакой магии. Теперь SortableStr("99")
это допустимый объект, который не является строкой, но демонстрирует желаемое поведение.
Комментарии:
1. Просто мысль, но можно ли заставить это работать с а
collections.UserString
? Я предполагаю, что нет, потому что в документах явно говорится, что он поддерживает «методы и операции» обычных строк, и я предполагаю, что эти магические методы относятся к этой категории.2. Фантастика, спасибо! Итак, что касается моего вопроса Полу М. ниже о том, как «ООН» реализует метод, я думаю, что ответ заключается
__lt__ = object.__lt__
в том, что это делает это эффективно. Интересный. Но определенно следует отметить, что наследование здесь не самый чистый подход.
Ответ №2:
Не уверен, правильно ли это , но, взглянув на документацию functools.total_ordering
, я понял, что это бросается в глаза:
Учитывая класс, определяющий один или несколько методов упорядочения с расширенным сравнением, этот декоратор класса предоставляет все остальное.
Акцент мой. Ваш класс наследуется __lt__
от str
, поэтому он не будет повторно реализован, total_ordering
так как он не отсутствует. Это мое лучшее предположение.
Комментарии:
1. Хорошо, да, я думаю, что ты прав. Так есть ли какое-то решение? Есть ли способ «отменить» реализацию метода без его перезаписи? Если нет, то, я думаю
functools.total_ordering
, просто не работает в этой ситуации?2. Похоже, @SilvioMayolo только что опубликовал более полный ответ, в котором пришел к тому же выводу. Взгляните!