Логические операторы в Python: and, or, not, короткое замыкание и типичные ошибки
Запрос для этой статьи: «логические операторы в Python». Если вы уверенно понимаете, как работают and, or, not, почему они иногда возвращают не булевы значения и как устроено короткое замыкание, вы сразу пишете чище и надёжнее. Разберём всё на практических примерах, с советами и частыми ошибками.
Быстрый обзор: and, or, not и «истинность» объектов
В Python любое выражение имеет «истинность» (truthiness): пустые контейнеры, 0, пустая строка, None — ложны; непустые объекты — истинны. Проверить можно функцией bool().
bool(0) # False
bool(1) # True
bool("") # False
bool("hi") # True
bool([]) # False
bool([1]) # True
bool(None) # False
Операторы работают так:
- not x — унарный оператор, возвращает True, если x ложен, иначе False.
- x and y — если x ложен, возвращает x, иначе возвращает y.
- x or y — если x истинен, возвращает x, иначе возвращает y.
Обратите внимание: and/or возвращают сам операнд, а не обязательно булево значение!
[] or [1] # [1]
0 or 5 # 5
"" or "fallback" # "fallback"
"hi" and 42 # 42
"" and "x" # ""
Короткое замыкание (short-circuit) и порядок вычисления
Python останавливает вычисления, как только результат ясен:
- В x and y при ложном x выражение y даже не вычисляется.
- В x or y при истинном x выражение y не вычисляется.
def left():
print("left")
return False
def right():
print("right")
return True
left() and right() # выведет: left (right не вызовется)
right() or left() # выведет: right (left не вызовется)
Практическая польза: можно безопасно вызывать «дорогие» функции только при необходимости.
user and user.is_active and send_bonus(user) # send_bonus вызовется только для активного пользователя
Шаблоны: «дефолтные значения» и безопасные проверки
Популярный трюк — задать значение по умолчанию через or:
username = input("Имя: ") or "Гость"
Важно: это срабатывает для любого ложного значения ("", 0, [] …). Если хотите заменить только None, используйте явную проверку:
value = None
result = value if value is not None else 10 # 0 или "" сохраняются как есть
any и all — удобнее, чем длинные связки and/or
any(iterable) — True, если хотя бы один элемент истинен; all(iterable) — True, если все истинны. Они тоже коротко замыкаются и принимают генераторы (без создания списков).
nums = [0, 3, 5, 0]
any(n > 4 for n in nums) # True
all(n >= 0 for n in nums) # True
# Валидация формы
name, email = "Alex", ""
if all([name, email]):
print("OK")
else:
print("Заполните все поля")
Совет: с большими данными используйте генераторы: all(cond(x) for x in data) — так не тратится память на список.
Цепочки сравнений: читаемо и без лишних повторов
Python поддерживает «математический» синтаксис цепочек: 0 <= x < 10 эквивалентно (0 <= x) and (x < 10), при этом x вычисляется один раз.
x = 5
0 <= x < 10 # True
x == 1 == 1 # False (эквивалентно x == 1 and 1 == 1)
3 <= x != 7 # True (эквивалентно 3 <= x and x != 7)
Используйте цепочки для диапазонов и каскадных проверок, это и короче, и безопаснее.
Приоритет операторов: not > and > or
Важно знать порядок вычисления: not выше, чем and, а and выше, чем or. Сравнения имеют приоритет выше, чем логические операторы. Поэтому:
not True or False # (not True) or False -> False
True and False or True # (True and False) or True -> True
not a == b # то же, что not (a == b)
(a != b) # эквивалентно более читаемо
С сомнениями — ставьте скобки. Так вы избавитесь от неоднозначности и ошибок чтения.
Логические vs побитовые: and/or против & и |
Не путайте логические операторы с побитовыми для чисел и элементно‑логическими для массивов (NumPy/Pandas).
- and/or работают с истинностью объектов и коротко замыкаются.
- & и | — побитовые/поэлементные, не делают короткого замыкания.
# Числа: побитовые операции
5 & 3 # 1
5 | 2 # 7
# NumPy/Pandas: объединение булевых масок — только & и |, и ОБЯЗАТЕЛЬНО скобки!
# (df["a"] > 0) & (df["b"] < 5) — правильно
# df["a"] > 0 & df["b"] < 5 — ошибка из-за приоритета операторов
Векторные библиотеки запрещают and/or для массивов, т.к. «истинность» массива неоднозначна — используйте & и | со скобками.
Частые ошибки и как их избежать
- Сравнение с True/False:
if is_ready == True:— избыточно. Пишитеif is_ready:илиif not is_ready:. - Подмена легитимных «ложных» значений:
x or defaultзаменит 0, "", [] и т.д. Если нужно заменить только None —x if x is not None else default. - «and-or» как тернарный оператор:
a and b or cломается, когда b ложен (возвращается c). Используйтеb if a else c. - Сравнение с None:
if x == None— неверный стиль. Пишитеx is Noneилиx is not None. - Ошибка «x == 'a' or 'b'»: это всегда истинно, т.к.
'b'— истинный операнд. Правильно:x in ('a', 'b'). - not in: пишите
x not in y, а неnot x in y— так читаемее и соответствует грамматике языка.
x = "b"
x == "a" or "b" # True (не то, что вы хотите)
x in ("a", "b") # Правильно
value = 0
value or 10 # 10 (вдруг 0 — валидное значение!)
value if value is not None else 10 # 0 сохранится
Идиомы и микропаттерны с логическими операторами
- Гвард-блоки: быстро выйти при некорректных условиях.
def send(user):
if not user or not user.email:
return # нечего делать
# основная логика
- Ленивая инициализация:
config = cached_config or load_config_from_file() # файл читается только при отсутствии кеша
- Компактная валидация:
def is_valid_user(u):
return all([u.name, u.email, u.age and u.age >= 18])
- Безопасные цепочки вызовов (когда может быть None):
# Возьмём домен email, если user и email существуют
domain = user and user.email and user.email.split("@")[-1]
# Лучше — тернарный/паттерн проверки:
domain = user.email.split("@")[-1] if (user and user.email) else None
Немного о производительности
Короткое замыкание экономит время и ресурсы. Простой пример с timeit:
import timeit
setup = """
def heavy():
s = 0
for i in range(10_000):
s += i*i
return s
x = 0
"""
print(timeit.timeit("x and heavy()", setup=setup, number=1000)) # heavy() почти не вызывается
print(timeit.timeit("x or heavy()", setup=setup, number=1000)) # heavy() вызывается всегда (x=0)
Вывод: правильно расставленные условия реально экономят время, особенно при «дорогих» вычислениях.
Чек‑лист по логическим операторам в Python
- Помните: and/or возвращают не True/False, а операнды.
- Используйте короткое замыкание для «ленивых» вычислений.
- Для «дефолтов» через or учитывайте ложные, но валидные значения (0, "").
- Для массива условий — any/all с генераторами.
- Для диапазонов — цепочки сравнений: 0 <= x < 10.
- Не путайте and/or с побитовыми & и |; в Pandas/NumPy нужны & и | со скобками.
- Проверки с None делайте через is/is not.
- Сомневаетесь в приоритете — ставьте скобки.
Что дальше
Если хотите системно прокачать основы, практиковаться на задачах и быстро перейти к реальным проектам, посмотрите пошаговый курс «Python с Нуля до Гуру» — стартуйте уже сегодня. Курс структурирован от базовых концепций до продвинутых приёмов, с практикой и обратной связью.
Освоив логические операторы в Python, вы избежите массы скрытых багов и напишете более выразительный код — это один из самых выгодных навыков для новичков и продолжающих разработчиков.
-
Создано 02.03.2026 17:01:39
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.