Что на самом деле возвращают and и or

Во многих языках программирования логические операторы всегда дают True или False. Python устроен иначе. Операторы and и or возвращают один из своих операндов целиком, без преобразования в булев тип. Оператор and сначала проверяет левый операнд. Если тот ложный, вычисление останавливается, и результатом становится именно этот левый операнд — правый Python даже не трогает. Если левый операнд истинный, Python переходит к правому и возвращает уже его, каким бы он ни был.

У or логика зеркальная. Если левый операнд истинный, оператор сразу возвращает его, а правый операнд не вычисляется вовсе. Если левый ложный, Python идёт дальше и отдаёт значение правого операнда, снова без всякого приведения к True или False. Разница особенно заметна на конкретных примерах. Выражение «5 and 0» вернёт 0, потому что 5 истинно и Python переходит ко второму операнду. Выражение «0 and 5» остановится на первом шаге и вернёт 0 — число 5 в этом вычислении вообще не участвует. С or картина обратная. «5 or 0» даёт 5, а «0 or 5» тоже даёт 5, но уже за счёт правого операнда.

Почему это не то же самое, что True и False

Функция bool() честно превращает любое значение Python в True или False по своим правилам. bool(5 and 0) выдаст False, потому что ноль в логическом контексте ложен. Сам оператор and при этом вернул значение 0 — число, а не логический флаг, и это два разных действия одно за другим. В условии if разница почти незаметна — интерпретатор сам решает, вести себя с результатом как с истиной или с ложью. Разница проявляется там, где результат and или or сохраняют в переменную или возвращают из функции. Переменная получит конкретный объект — строку, число, список, словарь или None — вместо булева типа, и дальнейший код должен быть готов работать именно с этим объектом.

Отсюда родилась частая практика — использовать or для подстановки значения по умолчанию. Запись «имя = введённое_имя or «Гость»» работает так. Если пользователь ввёл непустую строку, она истинна, и переменная имя получит именно её. Если строка пустая, Python сочтёт её ложной и вернёт вторую часть — слово «Гость». Приём короткий и читаемый, но плохо ведёт себя на данных, где законным значением бывает 0 или пустой список: оператор молча заменит настоящий, но «ложный» результат значением по умолчанию, и это стоит держать в голове перед тем, как применять приём к числовым полям.

Короткое замыкание бережёт лишние вычисления

Слово «короткое» в термине short-circuit означает, что Python не обязан считать оба операнда при каждом вычислении. Как только результат всего выражения уже понятен по одному операнду, второй пропускается целиком, вместе с любыми действиями, которые он мог бы выполнить внутри себя. Это свойство удобно использовать как защиту от ошибок во время выполнения программы: сначала проверяют условие, которое гарантированно безопасно вычислить, а следом — то, что могло бы упасть с исключением на плохих данных.

  • Проверка «список and список[0] > 0» не обращается к несуществующему элементу — если список пуст, and останавливается на первом операнде, и до список[0] дело не доходит
  • Проверка «делитель and числитель / делитель» убережёт от деления на ноль тем же способом
  • Проверка «значение в кэше or результат медленного расчёта» вызовет медленную функцию только тогда, когда в кэше действительно ничего не нашлось

Какие значения Python считает ложными

Логический контекст в Python шире, чем просто True и False. Ложными Python признаёт число 0 и 0.0, пустую строку, пустой список, пустой кортеж, пустой словарь, пустое множество и специальное значение None. Само слово False тоже ложно, разумеется. Все остальные значения — любое непустое число, непустая строка, непустая коллекция — Python в логическом контексте считает истинными, даже если по смыслу они выглядят «отрицательными»: например, число минус один или строка из одного символа «0».

Как проверить это самостоятельно

Функция bool() показывает результат наглядно. bool(0), bool(«»), bool([]) и bool(None) выведут False, а bool(-1), bool(«0») и bool([0]) выведут True. Список из одного нулевого элемента истинен, хотя хранит только ноль, потому что истинность списка зависит от того, пуст он или нет, а содержимое элементов роли тут не играет. Ту же логику стоит проверить и на собственных примерах — пустом словаре, кортеже из одного нуля, строке из одного пробела. Пробел в отличие от пустой строки истинен, потому что формально это символ, а отсутствием символов пустая строка как раз и является.

Типичная ошибка — if x == 1 or 2

Строка «if x == 1 or 2» выглядит так, будто проверяет, равен ли x числу 1 или числу 2, но оператор == связывает только x и 1, и всё выражение читается интерпретатором как (x == 1) or 2. Число 2 в логическом контексте истинно всегда, независимо от значения переменной x. Оператор or, увидев истинный второй операнд после ложного или истинного первого, вернёт итоговое значение выражения так, что любое ненулевое число внутри if равносильно True. В итоге условие срабатывает при любом x, даже когда переменная равна 47 или минус пять, а автор кода ожидал проверки на конкретные два числа.

Исправить запись можно двумя способами, и оба явно повторяют сравнение для каждого значения. Первый способ — расписать оба условия целиком: «if x == 1 or x == 2». Второй короче и обычно читается лучше: «if x in (1, 2)», где оператор in проверяет принадлежность значения кортежу или списку. При десятке и больше вариантов второй способ особенно выигрывает в компактности — список допустимых значений разрастается в кортеж или множество, а само условие остаётся одной строкой кода.

Где это свойство работает на пользу программиста

  • Значение по умолчанию через or — когда пустая строка или None действительно означают «данных нет»
  • Защита от обращения к несуществующему индексу или ключу через and перед основной проверкой
  • Цепочка условий вида a and b and c — вычисление останавливается на первом ложном звене и не тратит время на остальные
  • Быстрая проверка «хотя бы одно условие верно» через a or b or c без развёрнутого if-elif-else

Цепочку из нескольких and и or Python читает слева направо с учётом того, что and связывает операнды теснее, чем or. Выражение «a or b and c» интерпретатор прочитает как a or (b and c), а не как (a or b) and c — скобки здесь расставляет сам приоритет операторов, а не порядок написания в строке. Когда цепочка длинная и приоритет неочевиден с первого взгляда, явные скобки экономят время на чтении кода — и своё, и чужое, особенно спустя месяцы после написания.

Частые вопросы

Можно ли просто заменить if на and и or

Иногда да, но только для коротких выражений с понятным результатом, например «x = value or default». Для сложной логики с несколькими ветками обычный if читается яснее и меньше рискует спрятать ошибку вроде разобранной выше.

Что вернёт длинная цепочка a and b or c

Python вычислит a and b по обычным правилам and, и результат этого шага станет левым операндом для or. Если a and b дало ложное значение, оператор or перейдёт к c и вернёт уже его.

Почему bool(0 and «текст») равен False, если and вернул 0

bool() применяется к результату отдельным шагом уже после вычисления and. Сам and честно вернул число 0, а bool() лишь показало, что это число ложно в логическом контексте — это два разных действия подряд, а не одно.

Как отличить ложные и истинные значения без вызова bool()

Достаточно запомнить короткий список из шести случаев — 0, 0.0, пустая строка, пустая коллекция, None и False. Всё остальное Python в условии if сочтёт истинным.