Примеры идут от простого к сложному: сначала правило «что считается истиной», затем сравнения и логические операторы по отдельности, затем их совместная работа в реальных условиях, и в конце — типичные ловушки: короткое замыкание, приоритет операторов и разница между == и 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, потому что строка и число сравниваются только по типу и значению одновременно, без неявного приведения.
Что делать дальше
Посмотрите файлы репозитория — там те же идеи в исходном виде из лекции, затем переходите к заданиям. Если что-то не запустилось — загляните в типичные ошибки.