Почему sorted(items) == items при __lt__ всегда возвращает False

Почему sorted(items) == items при __lt__ всегда возвращает False

@python_quiz

Разберем этот квиз

Кратко: в приведённом коде сортировка вернёт True потому, что метод сравнения __lt__ всегда отдаёт False, а Timsort в Python стабилен — порядок равнозначных элементов сохраняется.

Исходный код

python

Вывод этой программы — True.

Пошаговый разбор: что происходит при сортировке

  1. sorted(items) вызывает алгоритм сортировки (в CPython — Timsort).
  2. Для определения порядка между двумя объектами используется вызов a.__lt__(b).
  3. В нашем классе __lt__ всегда возвращает False для любой пары.
  4. Если для пары (a, b) и (b, a) оба вызова __lt__ дают False, элементы считаются «не меньшими» друг друга — по сути равными с точки зрения порядка.
  5. Timsort — стабильный: при равенстве ключей он сохраняет исходный относительный порядок элементов.
  6. Поэтому отсортированный список содержит те же объекты в том же порядке, что и исходный.
  7. Оператор == для списков сравнивает элементы попарно с помощью их __eq__. Поскольку объекты одинаковы по значению v (или, точнее, те же объекты в том же порядке), сравнение возвращает True.

Важно: sorted возвращает новый список, но элементы в нём — те же самые объекты (те же ссылки), поэтому и сравнение через __eq__ проходит ожидаемо.

Почему не происходит TypeError

TypeError при сортировке возникает, если сравнение между элементами не определено (например, __lt__ возвращает NotImplemented или отсутствует и сравнение стремится быть выполнено, но не может). В нашем случае __lt__ явно возвращает булево False — это валидный результат, и исключения не возникает.

Пример, когда будет TypeError:

python

Этот код приведёт к TypeError: '<' not supported between instances of 'Item2' and 'Item2', потому что возвращаемое NotImplemented заставляет интерпретатор искать обратный вызов или посчитать сравнение невозможным.

Анализ вариантов (почему другие мысли неверны)

  • "Выведет False, потому что __lt__ всегда возвращает False" — неверно, потому что возвращение False не означает, что элементы переставляются; при равенстве ключей стабильная сортировка сохраняет исходный порядок.
  • "Выведет True, потому что sorted не использует __lt__" — неверно, sorted использует __lt__ для упорядочивания, просто в данном случае __lt__ всегда False.
  • "Будет исключение TypeError из-за некорректного __lt__" — неверно для данного кода: __lt__ корректно возвращает булево значение; TypeError возник бы, если бы __lt__ возвращал NotImplemented или был отсутствующим/неподдерживаемым для сравнения.

Небольшие дополнения по устойчивости и консистентности сравнений

  • Стабильность сортировки (Timsort) — ключевой фактор: если набор элементов «равен» с точки зрения отношения <, их относительный порядок не меняется.
  • Важно, чтобы поведение __lt__ и __eq__ было логически совместимо. Если __eq__ говорит, что элементы не равны, а __lt__ делает их «равными» (все сравнения False), это может привести к неожиданному порядку.
  • Лучше реализовывать сравнение через ключи: вместо сложной логики в __lt__ писать key-функцию для sorted, это проще и надёжнее.

Практическая рекомендация

Если вам нужно определить порядок для объектов, реализуйте либо:

  • __lt__ в соответствии с ожидаемой транзитивностью/антисимметрией, либо
  • используйте аргумент key в sorted, например:
python

Это гораздо понятнее и безопаснее, чем оставлять __lt__ с неконсистентным поведением.

Вывод

Когда __lt__ всегда возвращает False, элементы считаются равными для цели сортировки, а стабильный алгоритм сохраняет исходный порядок — поэтому сравнение отсортированного списка с оригиналом возвращает True. TypeError в таком коде не возникает, потому что сравнение возвращает валидное булево значение.

Report Page