💻 Примеры: читаем логические выражения

← К оглавлению урока

⚡ Пять фактов о bool на одном экране

x = 5
print(1 < x <= 10)          # True   — двойное неравенство
print(bool("False"))         # True   — непустая строка = истина
print(0 or "default")        # default — or возвращает операнд, не bool
print(not x > 3)             # False  — сначала сравнение, потом not
print([1, 2] == [1, 2])      # True   — == сравнивает значение, is — объект
КонструкцияЧто делаетЧастая ловушка
bool(x)приводит к True/Falsebool("False") — True
and / orоба / хотя бы одно условиевозвращают операнд, а не bool
notменяет на противоположноеприоритет ниже сравнений
a < x <= bдиапазон одной записьюэквивалент a < x and x <= b
== / isравенство значений / тот же объектlist1 == list2, но не is
Топ-3 ошибки: true вместо True · print(a = b) вместо print(a == b) · считать and/or функциями, возвращающими только True/False.

Примеры идут от простого к сложному: сначала правило «что считается истиной», затем сравнения и логические операторы по отдельности, затем их совместная работа в реальных условиях, и в конце — типичные ловушки: короткое замыкание, приоритет операторов и разница между == и is. Весь вывод в комментариях получен запуском кода.

Пример 1. Что Python считает истиной

Любой объект можно проверить функцией bool(). Ложными считаются только «пустые» значения: 0, None, пустые строка/список/словарь/кортеж. Всё остальное — включая отрицательные числа и строку из текста "False" — истина.

# truthy_falsy.py
values = [42, 0, -2, "", "0", "Python", "False", [], {}, (), None, 3.14]
for v in values:
    print(repr(v), "->", bool(v))
# 42 -> True
# 0 -> False
# -2 -> True
# '' -> False
# '0' -> True
# 'Python' -> True
# 'False' -> True
# [] -> False
# {} -> False
# () -> False
# None -> False
# 3.14 -> True

Что происходит: -2 истинно — «ложно» только само число 0, а не отрицательные значения. Строка '0' и строка 'False' непустые, поэтому обе истинны — Python не читает их содержимое, только длину.

Пример 2. Операторы сравнения возвращают bool

# comparisons.py
a, b = 10, 5
print(a == b, a != b, a > b, a < b, a >= b, a <= b)
# False True True False True False

print("apple" == "Apple")   # False — регистр важен
print("apple" < "banana")   # True  — сравнение по алфавиту (кодам символов)
print(3 == 3.0)              # True  — int и float сравниваются по значению

Что происходит: строки сравниваются посимвольно по кодам, поэтому регистр меняет результат, а "apple" < "banana" — это, по сути, алфавитный порядок. int и float с одинаковым значением равны, хотя их типы разные.

Пример 3. and/or с коротким замыканием

and останавливается на первом ложном операнде, or — на первом истинном. Второй операнд в этих случаях вообще не вычисляется — это видно, если вставить в него печать.

# short_circuit.py
def probe(name, value):
    print("  проверяю", name)
    return value


print(probe("a", False) and probe("b", True))
#   проверяю a
# False              — probe("b", ...) не вызывался

print(probe("c", True) or probe("d", True))
#   проверяю c
# True               — probe("d", ...) не вызывался

print(not probe("e", True))
#   проверяю e
# False

Что происходит: если результат уже понятен по первому операнду, второй — лишняя работа, и Python её пропускает. Это не просто оптимизация: на неё можно опираться, например чтобы избежать ошибки в проверке вида x != 0 and 10 / x > 1.

Пример 4. and/or возвращают операнд, а не всегда bool

Частое заблуждение: and/or всегда дают True/False. На самом деле они возвращают один из исходных операндов — тот, на котором остановилось вычисление.

# operand_return.py
print(0 or "default")       # default — 0 ложно, берём второй операнд
print("value" or "default") # value   — первый операнд уже истинный
print("" and "unused")      # (пусто) — "" ложно, and сразу его и вернул
print(5 and "last")         # last    — 5 истинно, берём второй операнд

name = "" or None or "Гость"
print(name)                 # Гость
Практическое применение: цепочка a or b or c — короткий способ взять первое «непустое» значение, что часто используют для значений по умолчанию. В более новом коде для этого также подходит оператор x if x else default или, для None конкретно, морж-подобные конструкции — но сам приём с or из лекции работает и сегодня.

Пример 5. Приоритет: not выше and, and выше or

# priority.py
a2, b2, c2 = False, True, True

print(not a2 or b2 and c2)          # True
print((not a2) or (b2 and c2))      # True  — то же самое со скобками
print(not (a2 or b2) and c2)        # False — скобки меняют результат

Что происходит: без скобок not a2 or b2 and c2 читается как (not a2) or (b2 and c2) — not и and связывают операнды теснее, чем or. Стоит переставить скобки вручную — как в третьей строке — и результат меняется. Для нетривиальных условий скобки лучше ставить явно, не полагаясь на память о приоритете.

Пример 6. Двойные неравенства как диапазон

# chained_comparisons.py
x = 5
print(1 < x <= 10)          # True
print(1 < x and x <= 10)    # True — тот же смысл, длиннее запись
print(0 <= x < 5)           # False — 5 не входит в [0, 5)

age = 17
print(0 <= age < 18)        # True — типичная проверка возрастной границы

Что происходит: в Python можно писать до сих пор непривычную для других языков цепочку a < x <= b — она разворачивается в a < x and x <= b, но короче и без риска перепутать переменную во второй половине.

Пример 7. Логика в реальных условиях: погода, отпуск, тариф

Три примера из лекции: сборка сложного условия из простых, отрицание условия и условие, которое одновременно и логическое, и арифметическое — превращение bool в множитель 0/1 через int().

# real_life_conditions.py
good_weather = True
is_weekend = False
free_time = True
can_go_out = (good_weather or is_weekend) and free_time
print(can_go_out)   # True

have_work = True
enough_money = True
can_go_vacation = not have_work and enough_money
print(can_go_vacation)   # False — работа мешает, даже если деньги есть

used_mb = 400
extra_mb = used_mb - 500
total_price = 30 + extra_mb * 0.2 * int(used_mb > 500)
print(total_price)   # 30.0 — лимит не превышен, доплаты нет

used_mb2 = 700
extra_mb2 = used_mb2 - 500
total_price2 = 30 + extra_mb2 * 0.2 * int(used_mb2 > 500)
print(total_price2)  # 70.0 — за 200 МБ сверх лимита начислена доплата

Что происходит: int(used_mb > 500) превращает bool в 0 или 1 — приём, которым в тарифе включают или выключают доплату без if. Это работает, потому что в Python bool — подтип int.

Пример 8. == сравнивает значение, is — тот же объект

Тема не входит в исходный конспект урока напрямую, но естественно продолжает сравнения: важно различать «равны по значению» и «это один и тот же объект в памяти».

# equality_vs_identity.py
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2)   # True  — содержимое одинаковое
print(list1 is list2)   # False — это два разных объекта в памяти

list3 = list1
print(list1 is list3)   # True — list3 ссылается на тот же объект

a3 = None
print(a3 == None)   # True
print(a3 is None)   # True — но так писать не принято
⚠️ Проверить по документации: разница между == и is выходит за рамки лекции урока 06. Идиоматично проверять None именно через is None, а не == None — см. Identity comparisons и PEP 8.

Пример 9. Фильтрация и проверка условия по коллекции

Логические выражения используются не только по одному значению — any()/all() сворачивают целую коллекцию условий в один bool. Тема подробно раскрывается позже, здесь — естественное продолжение сравнений урока.

# any_all_scores.py
scores = [55, 91, 40, 78, 60, 33]
passing = [s for s in scores if s >= 60]
print(passing)                          # [91, 78, 60]

print(all(s >= 0 for s in scores))       # True  — все баллы неотрицательны
print(any(s >= 90 for s in scores))      # True  — хотя бы один отличник
print(all(s >= 60 for s in scores))      # False — не все прошли порог

Связь с темой: s >= 60 — то же самое сравнение, что и в примере 2, просто применённое к каждому элементу списка. Подробнее про any/all и их короткое замыкание — в уроке 42.

Пример 10. Ошибка = вместо == внутри print()

Частая опечатка — использовать оператор присваивания там, где нужно сравнение. Внутри вызова функции a = b не превращается в ошибку синтаксиса, а трактуется как именованный аргумент — и падает уже во время выполнения.

# assign_vs_compare.py
a4 = 5
b4 = 5
try:
    print(a4 = b4)
except TypeError as error:
    print("TypeError:", error)
    # 'a4' is an invalid keyword argument for print()
print(a4 == b4)   # True — а вот так сравнение работает

print("5" == 5)          # False — строка и число не равны без приведения
print(int("5") == 5)     # True  — после приведения типов равны

Что происходит: Python пытается прочитать a4 = b4 внутри скобок как именованный аргумент print(a4=b4), а не как сравнение — отсюда неожиданный TypeError вместо привычного SyntaxError. Второй урок ошибки: "5" == 5 — False, потому что строка и число сравниваются только по типу и значению одновременно, без неявного приведения.

Что делать дальше

Посмотрите файлы репозитория — там те же идеи в исходном виде из лекции, затем переходите к заданиям. Если что-то не запустилось — загляните в типичные ошибки.