В условии передан пользовательский объект без явной проверки: как Python определяет, считать его истинным?
Python сначала вызывает у объекта метод bool(). Если его нет, используется len(): нулевая длина означает ложность, ненулевая — истинность. Если оба метода не определены, объект считается истинным; исключение из этих методов не преобразуется в False, а передаётся вызывающему коду.
Такой протокол позволяет проверять в условии разные типы единообразно: пустые коллекции, нулевые числа и пользовательские объекты могут участвовать в логике без специальных проверок их конкретного типа.
Подход основан на общей модели объектов Python: объект сам сообщает, как интерпретировать его в булевом контексте. Это поддерживает полиморфизм и уменьшает зависимость кода от конкретных классов.
Неверное понимание истинности приводит к ошибкам в проверках конфигурации, результатов поиска и контейнеров. Например, условие может отличать отсутствие значения от пустого значения не так, как ожидает разработчик.
Важно также учитывать, что проверка истинности — не проверка типа и не проверка равенства True. Объект может быть истинным, не являясь экземпляром bool, а пользовательский метод __bool__() может намеренно завершаться ошибкой при неоднозначном смысле проверки.
При вычислении объекта в булевом контексте Python действует такой порядок:
Метод bool() должен вернуть объект типа bool. Метод len() должен вернуть неотрицательное целое число; ноль означает ложность. Если метод возвращает неподходящее значение или длина отрицательна, Python возбуждает исключение, а не делает объект ложным.
Минимальный пример:
Здесь Python не знает специального смысла класса Batch; он использует его протокол длины. Если добавить __bool__(), он будет иметь приоритет над __len__().
Булевы операции тоже используют этот механизм. not возвращает логическое значение, а and и or возвращают один из исходных операндов, предварительно проверяя их истинность. Поэтому выражение с or может вернуть не True или False, а исходный объект.
Практическое ограничение заключается в выборе смысла. Если пустой объект должен считаться допустимым значением, не следует бездумно писать проверку его истинности: лучше явно проверить is None, длину или отдельное свойство состояния.
В сервисе объект конфигурации мог существовать, но содержать пустой набор правил. Разработчик использовал его в условии и трактовал ложный результат как отсутствие конфигурации. Поскольку класс определял __len__() по числу правил, пустая, но корректная конфигурация ошибочно считалась не заданной.
Рассматривались два варианта. Первый — убрать __len__(): это устраняло неожиданную ложность, но лишало объект естественной поддержки операций, связанных с размером. Второй — оставить протокол длины и проверять наличие конфигурации явно, например через is None или отдельное свойство enabled.
Выбрали второй вариант: __len__() сохранили для семантики количества правил, а проверку существования сделали явной. Это разделило два разных понятия — «объект отсутствует» и «объект пуст» — и предотвратило ошибочное отключение настроек.
Что произойдёт, если одновременно определить bool() и len()?
Будет вызван bool(). Метод __len__() используется только как запасной механизм, если __bool__() отсутствует. Поэтому добавление __bool__() может изменить поведение уже существующего класса, даже если его длина не изменилась.
Почему исключение из bool() не превращается в ложный результат?
Истинность — это операция, которая может требовать проверки инвариантов объекта. Если объект не способен однозначно определить своё состояние, скрытое преобразование ошибки в False привело бы к незаметным неверным ветвлениям. Поэтому исключение распространяется наружу, позволяя вызывающему коду обработать проблему явно.
Чем проверка истинности отличается от сравнения с True?
Проверка истинности вызывает протокол __bool__() или __len__(), тогда как сравнение с True выполняет операцию равенства и может использовать __eq__(). Истинный объект не обязан быть равен True, поэтому для ветвления обычно используют сам объект в условии, а сравнение с True применяют только при явно требуемой проверке значения булевого типа.