Двенадцать примеров закрывают весь блок уроков 08 — операторы ветвления, 09 — практикум 2 и 10 — математические функции, хранение чисел, модули: от простого if-elif-else до точных денежных расчётов через Decimal. Порядок — от простого к сложному, весь вывод в комментариях получен реальным запуском кода.
Пример 1. Возрастная категория: цепочка if-elif-else
Базовый инструмент ветвления: проверить несколько взаимоисключающих условий и выбрать одну ветку.
age = 15
if age <= 2:
category = "Младенец"
elif age <= 12:
category = "Ребёнок"
elif age <= 17:
category = "Подросток"
else:
category = "Взрослый"
print(f"Категория: {category}.")
# Категория: Подросток.
Что происходит: условия проверяются сверху вниз, выполняется первая истинная ветка. Порядок условий важен: достаточно проверить верхнюю границу каждой категории, потому что более младшие значения уже отсеяны предыдущими elif.
Пример 2. Неявное преобразование в bool (truthy/falsy)
В условии if Python сам решает, что считать «правдой». Это экономит код, но нужно точно знать список «ложных» значений.
name = "Alice"
if name:
print(f"Привет, {name}!")
value = 0
if value:
print("Ноль не выведется")
else:
print("value ложно, ветка else")
items = []
if items:
print("Список непуст")
else:
print("Пустой список — falsy")
print(bool(""), bool("0"), bool(0), bool(0.0), bool(None), bool([]), bool({}))
# Привет, Alice!
# value ложно, ветка else
# Пустой список — falsy
# False True False False False False False
Что происходит: False дают только 0, 0.0, пустая строка "", None и пустые коллекции ([], {}). Обратите внимание на bool("0") — строка с одним нулём непустая, поэтому она True.
Пример 3. Независимые if против цепочки if-elif-else
Несколько отдельных if проверяются независимо и могут сработать все сразу; в цепочке if-elif-else выполняется только одна ветка.
x = 8
print("Независимые if:")
if x > 5:
print("x больше 5")
if x < 10:
print("x меньше 10")
if x % 2 == 0:
print("x чётное")
print("Цепочка if-elif-else:")
if x > 5:
print("x больше 5 (elif-ветка)")
elif x < 10:
print("x меньше 10 (elif-ветка) — не выполнится, т.к. первая ветка уже сработала")
else:
print("x меньше или равно 5")
# Независимые if:
# x больше 5
# x меньше 10
# x чётное
# Цепочка if-elif-else:
# x больше 5 (elif-ветка)
Что происходит: все три независимых if вывели строку, потому что каждое условие проверялось само по себе. В цепочке напечаталась только первая истинная ветка — вторая проверка x < 10 вообще не выполнялась, хотя тоже истинна.
Пример 4. Вложенные условия → плоская структура (guard clauses)
Вложенные if работают, но растут вширь с каждым уровнем. Guard clauses, and/or/not и цепочные сравнения делают ту же логику плоской и читаемой.
role = "admin"
access = False
# Вложенный вариант
if role == "admin":
if access:
print("Вложенный: доступ разрешён")
else:
print("Вложенный: доступ запрещён")
else:
print("Вложенный: недостаточно прав")
# Плоский вариант через guard clauses и логические операторы
if role != "admin":
print("Плоский: недостаточно прав")
elif not access:
print("Плоский: доступ запрещён")
else:
print("Плоский: доступ разрешён")
# Цепочное сравнение вместо and
temperature = 23
if 18 <= temperature <= 26:
print("Комфортная температура (цепочное сравнение)")
age = 8
is_child = 0 <= age < 18
print("is_child:", is_child)
# Вложенный: доступ запрещён
# Плоский: доступ запрещён
# Комфортная температура (цепочное сравнение)
# is_child: True
Что происходит: оба варианта дают одинаковый результат «доступ запрещён», но плоский читается без подсчёта отступов. Выражение 18 <= temperature <= 26 — это цепочное сравнение, эквивалент 18 <= temperature and temperature <= 26, только короче.
Пример 5. Тернарный оператор
Для простого выбора между двумя (или несколькими) значениями тернарный оператор компактнее полноценного if.
age = 20
status = "Совершеннолетний" if age >= 18 else "Несовершеннолетний"
print(status)
score = 85
grade = "Отлично" if score > 90 else "Хорошо" if score > 75 else "Удовлетворительно"
print(grade)
n = 7
parity = "чётное" if n % 2 == 0 else "нечётное"
print(f"{n} — {parity}")
# Совершеннолетний
# Хорошо
# 7 — нечётное
Что происходит: вложенный тернарный оператор для grade допустим, но это верхняя граница читаемости — третий уровень вложенности лучше переписать через if-elif-else.
Пример 6. match-case для HTTP-статусов
match-case (Python 3.10+) удобен, когда одно значение нужно сравнить с набором конкретных шаблонов — читается яснее длинной цепочки elif.
def status_message(status):
match status:
case 200:
return "OK"
case 400:
return "Bad Request"
case 401 | 403 | 404:
return "Client Error"
case 500 | 502 | 503:
return "Server Error"
case _:
return "Unknown"
for s in (200, 404, 503, 999):
print(s, "->", status_message(s))
# 200 -> OK
# 404 -> Client Error
# 503 -> Server Error
# 999 -> Unknown
match-case доступен только с Python 3.10. Убедитесь, что ваша версия поддерживает эту конструкцию — Python Docs — match statement.
Пример 7. Сортировка трёх чисел: сравнения против sorted()
Задача из практикума 09: научиться выражать порядок через сравнения, а не только вызывать готовую функцию.
a, b, c = 7, 2, 5
# Вручную через сравнения
if a <= b <= c:
result = (a, b, c)
elif a <= c <= b:
result = (a, c, b)
elif b <= a <= c:
result = (b, a, c)
elif b <= c <= a:
result = (b, c, a)
elif c <= a <= b:
result = (c, a, b)
else:
result = (c, b, a)
print("Вручную:", ", ".join(str(n) for n in result))
# Через встроенную сортировку
print("sorted():", ", ".join(str(n) for n in sorted([a, b, c])))
# Вручную: 2, 5, 7
# sorted(): 2, 5, 7
Что происходит: оба способа дают одинаковый результат. Ручной вариант — тренировка цепочных сравнений и полного перебора вариантов порядка; на практике для реальной сортировки почти всегда используют sorted().
Пример 8. Високосный год: комбинация and/or
Год високосный, если делится на 4, но не на 100, либо делится на 400 — классический пример, где скобки в логическом выражении обязательны.
for year in (2024, 1900, 2000, 2023):
is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
if is_leap:
print(f"{year} год является високосным.")
else:
print(f"{year} год не является високосным.")
# 2024 год является високосным.
# 1900 год не является високосным.
# 2000 год является високосным.
# 2023 год не является високосным.
Что происходит: 1900 делится на 100, но не на 400 — не високосный. 2000 делится и на 100, и на 400 — снова високосный. Без скобок вокруг and-части приоритет операторов дал бы неверный результат для части годов.
Пример 9. Встроенные математические функции и банковское округление
abs, max, min, pow, sum, round работают без импорта — это первый слой математики Python.
print(abs(-7))
print(max(1, 5, 3))
print(min(2, 8, 4, 1))
print(pow(2, 3))
print(sum([1, 2, 3, 4]))
print(round(4.567, 2))
# Банковское округление: до ближайшего чётного при дробной части 0.5
print(round(1.5), round(2.5), round(3.5), round(-1.5), round(-2.5))
# 7
# 5
# 1
# 8
# 10
# 4.57
# 2 2 4 -2 -2
Что происходит: round(2.5) дал 2, а не 3 — это стандарт IEEE 754 «округление до чётного», а не ошибка Python. Для школьного округления «всегда от нуля» нужен отдельный алгоритм (пример 10).
Пример 10. Модуль math: корни, факториал, округления, гипотенуза
Когда встроенных функций не хватает, подключают math: корни, факториал, округление вверх/вниз, константы. Здесь же — практическое применение в теореме Пифагора и в математическом округлении «от нуля».
import math
print(math.sqrt(25))
print(math.factorial(5))
print(math.ceil(4.2), math.floor(4.9))
print(round(math.pi, 4), round(math.e, 4))
a_leg, b_leg = 3.0, 4.0
hypotenuse = math.sqrt(a_leg ** 2 + b_leg ** 2)
print(f"Гипотенуза: {hypotenuse}")
def round_half_away_from_zero(x):
frac = x - int(x)
if x >= 0:
return math.ceil(x) if frac >= 0.5 else math.floor(x)
return math.floor(x) if frac <= -0.5 else math.ceil(x)
for value in (2.5, -2.5, 2.4, -2.4):
print(value, "->", round_half_away_from_zero(value))
# 5.0
# 120
# 5 4
# 3.1416 2.7183
# Гипотенуза: 5.0
# 2.5 -> 3
# -2.5 -> -3
# 2.4 -> 2
# -2.4 -> -2
Что происходит: катеты 3 и 4 дают гипотенузу ровно 5.0 — классическая пифагорова тройка. Функция round_half_away_from_zero использует math.ceil для положительной половины и math.floor для отрицательной, поэтому 2.5 и -2.5 округляются симметрично от нуля, в отличие от встроенного round() из примера 9.
Пример 11. Три способа импорта модуля
Модуль — обычный файл .py. Импортировать его можно целиком, частично или с псевдонимом — от выбора зависит, как потом обращаться к функциям.
import math
print(math.sqrt(16))
from math import sqrt, pi
print(sqrt(16), round(pi, 4))
import math as m
print(m.sqrt(16))
# 4.0
# 4.0 3.1416
# 4.0
Что происходит: все три способа дают один и тот же результат 4.0, но по-разному называют функцию: math.sqrt, просто sqrt или m.sqrt. from math import * тоже сработает, но не рекомендуется — импортирует все имена сразу и рискует затереть уже существующие переменные (например, свою pi).
Пример 12. Точность float и Decimal в денежном расчёте
Финальный пример объединяет тему хранения чисел с практической задачей: посчитать сумму со скидкой так, чтобы копейки не «поплыли».
import math
from decimal import Decimal
a = 0.1 + 0.2
print(a)
print(a == 0.3)
print(math.isclose(a, 0.3))
price = 19.99
quantity = 100
discount = 0.20 if quantity >= 100 else 0.0
total_float = price * quantity * (1 - discount)
print(f"float: {total_float}")
price_d = Decimal("19.99")
discount_d = Decimal("0.20")
total_decimal = price_d * quantity * (1 - discount_d)
print(f"Decimal: {total_decimal}")
# Неправильно: float уже внёс погрешность до Decimal
print(Decimal(0.1) + Decimal(0.2))
# 0.30000000000000004
# False
# True
# float: 1599.1999999999998
# Decimal: 1599.2000
# 0.3000000000000000166533453694
Что происходит: 0.1 + 0.2 == 0.3 ложно из-за двоичного представления float, а math.isclose сравнивает безопасно. Расчёт суммы на float дал «грязный» хвост 1599.1999999999998, а Decimal со строковыми литералами — ровно 1599.2000. При этом Decimal(0.1) (без кавычек) уже наследует погрешность float — строку нужно передавать всегда.