Статьи

Вся True и False об операторах or и and в Python

2025-08-04 16:25 Синтаксис Python

Давайте начнем с простого вопроса. Что выведет этот код?

print(5 or 0)

Многие ответят: True. Кажется, что это очевидно. 5 — это истина, 0 — ложь, True or False всегда будет True. Логично?

Логично. Но неверно.

На самом деле, Python выведет 5.

Если этот результат вас удивил, поздравляю — эта статья для вас. А если не удивил, останьтесь — мы дойдем до трюков, которые заставят вас уважительно кивнуть.

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

Часть 1: Truthy и Falsy. Что Python считает «правдой»?

Прежде чем мы вскроем механику or и and, нужно усвоить одно ключевое понятие: «истинность» любого объекта в Python. Каждый объект, будь то число, строка, список или None, можно оценить с точки зрения его «правдивости».

  • Falsy (ложные) значения: Это объекты, которые Python считает эквивалентом False в логическом контексте. Их немного, и их легко запомнить:

    • None
    • False
    • Ноль для любых числовых типов (0, 0.0, 0j, Decimal(0))
    • Пустые последовательности и коллекции ('', (), [], {}, set(), range(0))
  • Truthy (истинные) значения: Это абсолютно все остальные объекты. Любое ненулевое число, любая непустая строка, любой непустой список — все они «истинны».

Проверить это легко с помощью встроенной функции bool():

print(f"Число 5: {bool(5)}")          # -> True
print(f"Число 0: {bool(0)}")          # -> False
print(f"Строка 'hello': {bool('hello')}") # -> True
print(f"Пустая строка: {bool('')}")      # -> False
print(f"Пустой список: {bool([])}")     # -> False

Это основа, на которой строится вся логика. Когда вы пишете if my_list:, Python на самом деле за кулисами проверяет if bool(my_list):.

Часть 2: Главный секрет. or и and возвращают не булевы значения

А вот и главный твист, который всё меняет. Вопреки интуиции, операторы or и and не возвращают True или False. Они всегда возвращают один из своих операндов (то, что стоит слева или справа от оператора).

Этот механизм называется короткозамкнутыми вычислениями (short-circuit evaluation). Python ленив в хорошем смысле слова: он вычисляет ровно столько, сколько нужно для определения результата.

Как на самом деле работает оператор or

Правило для or: «Верни первый истинный (truthy) операнд, который встретишь, или последний, если все ложные».

Python смотрит на левый операнд.

  • Если он истинный (truthy), Python немедленно возвращает его значение и даже не смотрит на правый операнд. Зачем? Результат уже ясен.
  • Если левый операнд ложный (falsy), Python возвращает значение правого операнда, каким бы оно ни было.

Как на самом деле работает оператор and

Правило для and: «Верни первый ложный (falsy) операнд, который встретишь, или последний, если все истинные».

Логика обратная, но принцип тот же:

  • Если левый операнд ложный (falsy), Python немедленно возвращает его и игнорирует правый.
  • Если левый операнд истинный (truthy), Python возвращает значение правого операнда.

[!NOTE] Краткая памятка

  • x or y: Если x истинно, то x, иначе y.
  • x and y: Если x ложно, то x, иначе y.

Часть 3: Доказательство лени. Short-circuit в действии

Давайте докажем, что Python действительно "ленится" и не вычисляет правую часть выражения, если результат уже известен.

Для этого мы используем код, который гарантированно вызовет ошибку: int("hello").

# Тестируем `or`
# Левый операнд `5` - истинный. Python видит его и сразу возвращает.
# До `int("hello")` дело даже не доходит!
result = 5 or int("hello") 
print(f"Результат: {result}") # -> Результат: 5 (никакой ошибки!)

# А теперь поменяем их местами
# Левый операнд `0` - ложный. Python вынужден пойти дальше.
# Он пытается вычислить `int("hello")` и... падает с ошибкой.
try:
    result = 0 or int("hello")
except ValueError as e:
    print(f"Случилась ошибка: {e}") # -> Случилась ошибка: invalid literal for int()

Это и есть short-circuit. Python сэкономил время, не выполняя заведомо ненужную операцию.

То же самое происходит и с and:

# Тестируем `and`
# Левый операнд `0` - ложный. Python видит его и сразу возвращает.
# До `int("hello")` дело опять не доходит.
result = 0 and int("hello")
print(f"Результат: {result}") # -> Результат: 0 (никакой ошибки!)

Часть 4: От теории к практике. Элегантные паттерны использования

Понимание этой механики — не просто академический интерес. Это ключ к написанию более чистого, короткого и выразительного кода.

Паттерн №1: Значение по умолчанию (самый частый кейс)

Представьте, вам нужно запросить у пользователя имя и, если он ничего не ввел, присвоить значение «Аноним».

Стандартный, многословный способ:

name = input("Введите имя: ")
if not name:  # Проверяем, не является ли строка пустой (falsy)
    name = "Аноним"
print(f"Привет, {name}!")

Элегантный способ с or:

name = input("Введите имя: ") or "Аноним"
print(f"Привет, {name}!")

Если пользователь вводит имя, input() возвращает непустую строку (которая truthy). Оператор or видит это и немедленно возвращает ее. Если пользователь просто нажимает Enter, input() возвращает пустую строку '' (которая falsy). Оператор or, видя ложный операнд слева, возвращает то, что справа — "Аноним". Код стал короче, а намерение — яснее.

Паттерн №2: Решение алгоритмической задачки

А теперь — настоящая магия. Достаточно распространённая задачка: на вход подаётся целое число, далее нужно было сложить все его цифры. Если получается не однозначное число, то снова сложить его цифры, и так далее пока не получим однозначное.

Пример: digital_root(493193)

  1. 4 + 9 + 3 + 1 + 9 + 3 = 29
  2. 2 + 9 = 11
  3. 1 + 1 = 2 Результат: 2.

Стандартное решение в лоб выглядело бы как-то так, с использованием цикла или рекурсии:

def digital_root_naive(n):
    while n >= 10:
        n = sum(int(digit) for digit in str(n))
    return n

Работает. Но скучно и многословно. А теперь, зная, как работают or и and, можно использовать известное математическое свойство (связь результата с остатком от деления на 9) и написать вот это:

def digital_root_pro(n):
    return n % 9 or n and 9

Эта одна строчка делает то же самое. Давайте разберем, как, применяя наши знания шаг за шагом.

Деконструкция магии:

  • Случай 1: Число n не кратно 9 (и не равно 0). Например, n = 493193.

    • n % 9 равно 2. Это ненулевое число, то есть truthy.
    • В выражении (n % 9) or (n and 9) первая часть (2) истинна.
    • Python немедленно возвращает 2 и игнорирует все, что после or. Результат верный.
  • Случай 2: Число n кратно 9 (и не равно 0). Например, n = 18.

    • n % 9 равно 0. Это falsy.
    • В выражении (n % 9) or (n and 9) первая часть ложна. Python вынужден вычислить вторую часть: n and 9.
    • В n and 9 (что эквивалентно 18 and 9): левый операнд 18 — истинный. Значит, оператор and вернет правый операнд, то есть 9.
    • В итоге все выражение превращается в 0 or 9. Левый операнд ложный, значит or вернет правый — 9. Результат верный. Цифровой корень числа, кратного 9, — это 9.

Это элегантное применение фундаментальных знаний о языке и математике.

Часть 5: Бонусный уровень. Ловушка в цепочках сравнений

Кстати, есть еще одна область, где неявное использование and может подложить свинью. Смотрим на такую цепочку сравнений:

x = 15
print(10 < x < 20) # -> True

Дело в том, что Python обрабатывает цепочки сравнений не так, как кажется. Запись a < b < c — это синтаксический сахар для a < b and b < c. В нашем случае это (10 < x) and (x < 20). Проблемы начинаются, когда мы пытаемся сравнить результат с булевым значением напрямую и неочевидным образом:

# Казалось бы, должно быть True
print(10 < x < 20 == True) # -> False. Что?!

Почему False? Потому что Python обрабатывает это как одну длинную цепочку сравнений, связанных неявным and: (10 < x) and (x < 20) and (20 == True).

  • 10 < 15 -> True
  • 15 < 20 -> True
  • 20 == True -> False (так как True в числовом контексте это 1, а 20 != 1)

Вся цепочка с and становится ложной из-за последнего, неожиданного сравнения.

[!WARNING] Осторожно, грабли! Такое поведение может привести к трудноуловимым багам, когда часть логики оказывается неверной из-за неправильно составленной цепочки.

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

print((10 < x < 20) == True) # -> True

[!WARNING] А лучше никогда не сравнивайте результат логического выражения с True или False напрямую (if condition == True:). Это избыточно и может привести к ошибкам, как в примере выше. Правильный питоничный способ: if condition: или if not condition:.

Финальные выводы: от знания к мастерству

  1. Забудьте про True/False для or/and: Думайте о них как об операторах, которые возвращают один из операндов. Это изменит ваш подход к написанию логических выражений.
  2. Помните про Truthy и Falsy: Знание того, какие объекты Python считает истинными, а какие ложными — это база для понимания работы условных операторов и логики.
  3. Используйте short-circuit осознанно: Ленивость Python — ваш друг. Применяйте ее для создания чистого и идиоматичного кода, например, для присвоения значений по умолчанию.
  4. Опасайтесь неявных and: В цепочках сравнений всегда держите в голове, как Python их интерпретирует, и используйте скобки для группировки, чтобы избежать сюрпризов.

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