Условные конструкции: if, elif, else, тернарный и walrus
После работы с типами переходим к управлению потоком исполнения. В Python условные конструкции компактны и читаемы - без скобок вокруг условия и фигурных вокруг блока. В этом уроке разберём базовый if/elif/else, тернарный оператор для коротких условий и относительно новый оператор моржа :=.
Базовый if
age = 25
if age >= 18:
print("Совершеннолетний")
Блок отделяется отступом (4 пробела по PEP 8). Никаких фигурных скобок. Двоеточие после условия обязательно.
С веткой else:
if age >= 18:
print("Совершеннолетний")
else:
print("Несовершеннолетний")
И с цепочкой elif:
if age < 13:
category = "ребёнок"
elif age < 18:
category = "подросток"
elif age < 65:
category = "взрослый"
else:
category = "пенсионер"
elif это сокращение от else if. В отличие от Go, где есть else if, в Python вложенных конструкций нет - всё через одно ключевое слово.
Условие = любое выражение
В условии может быть что угодно, что приводится к bool:
if items: # коллекция не пустая
process(items)
if user.is_active: # атрибут truthy
send_email(user)
if not config.get("debug"): # ключ отсутствует или falsy
enable_optimizations()
if 0 < age < 150: # цепочка сравнений
save(age)
Последний пример - цепочка сравнений (chained comparison). Python поддерживает математическую нотацию a < x < b как сокращение для a < x and x < b, но с лучшей читаемостью и одним вычислением x.
Тернарный оператор
Для коротких условий есть однострочная форма:
status = "взрослый" if age >= 18 else "ребёнок"
Структура: <value_if_true> if <condition> else <value_if_false>.
Это выражение, не оператор - возвращает значение. Можно использовать в выражениях:
print(f"Доступ: {'разрешён' if user.authorized else 'запрещён'}")
discount = 0.1 if total > 1000 else 0
# Плохо
label = "A" if x > 90 else "B" if x > 80 else "C" if x > 70 else "F"
# Лучше
if x > 90:
label = "A"
elif x > 80:
label = "B"
elif x > 70:
label = "C"
else:
label = "F"
Правило: один тернарный - норма, два - граница, три - явно перебор.
Оператор моржа :=
С Python 3.8 появился оператор присваивания в выражении - walrus operator (получил имя из-за похожести на морду моржа):
# Без walrus - вычисляем дважды
if len(items) > 10:
print(f"Слишком много: {len(items)}")
# С walrus - одно вычисление
if (n := len(items)) > 10:
print(f"Слишком много: {n}")
Это полезно когда:
- Значение нужно для условия и для тела
- Вычисление дорогое
- Хочется избежать промежуточной переменной перед условием
Ещё типичный пример - чтение файла построчно с проверкой:
while (line := f.readline()):
process(line)
В этом коде readline() вызывается один раз за итерацию, его результат проверяется (пустая строка = конец файла) и используется в теле.
pass - пустой блок
В Python нельзя оставить блок пустым - получишь синтаксическую ошибку. Если нужна заглушка:
if config.debug:
pass # TODO: добавить отладочный вывод
else:
run_production_mode()
pass - официальная заглушка «ничего не делать». Не путать с ... (Ellipsis), который тоже работает как заглушка (часто в type stubs):
def not_implemented() -> int:
... # placeholder для будущей реализации
pass и ... эквивалентны как заглушки - pass чаще для блоков, ... для тел функций (соглашение типизаторов).
Распространённые ошибки
1. Сравнение с константой не в том порядке.
В C принято писать if (NULL == ptr) чтобы поймать опечатку = вместо ==. В Python это не нужно - присваивание = нельзя поставить в условие (if x = 5: - SyntaxError). Walrus := это отдельный оператор.
2. Использование is вместо == для значений.
# Плохо - может работать случайно из-за интернирования
if x is 100:
...
# Правильно
if x == 100:
...
Использовать is только для None, True, False.
3. Перекрытие встроенных имён.
# Плохо - переопределяет встроенную функцию
list = [1, 2, 3]
if list:
...
Используй другие имена: items, numbers, users. Линтер ругается на shadowing builtins.
Идиомы
Проверка на null с дефолтом:
name = user.name if user else "Anonymous"
# или короче
name = (user and user.name) or "Anonymous"
Маппинг через dict вместо elif:
# Длинный if/elif
def get_label(grade):
if grade == "A":
return "Отлично"
elif grade == "B":
return "Хорошо"
elif grade == "C":
return "Удовлетворительно"
else:
return "Неизвестно"
# Через dict
def get_label(grade):
labels = {"A": "Отлично", "B": "Хорошо", "C": "Удовлетворительно"}
return labels.get(grade, "Неизвестно")
Второй вариант декларативный и легче расширять - подробно про такой dispatch в уроке про словари.
Early return вместо вложенности:
# Плохо - глубокая вложенность
def process(user):
if user is not None:
if user.is_active:
if user.has_permission("write"):
do_work(user)
# Хорошо - guards в начале
def process(user):
if user is None:
return
if not user.is_active:
return
if not user.has_permission("write"):
return
do_work(user)
Это паттерн «guard clauses» - выходим из функции рано, чтобы основная логика была плоской. Тому же приёму посвящён урок «Ранние return и меньше вложенности».
Сравнение с другими языками
| Конструкция | Python | Go | PHP |
|---|---|---|---|
| if | if x > 0: | if x > 0 { | if ($x > 0) |
| else if | elif | else if | elseif |
| тернарный | a if cond else b | нет, через if/else | cond ? a : b |
| switch | match-case (3.10+) | switch | switch/match |
| chained compare | 0 < x < 10 | через AND | через && |
| walrus | := (3.8+) | := (declaration) | нет |
Python и Go используют :=, но в разных значениях: в Go - объявление переменной с выводом типа, в Python - присваивание внутри выражения.
Мини-задание
- Перепиши вложенный if с early return:
def can_buy(user, product):
if user.is_registered:
if user.balance >= product.price:
if product.in_stock:
return True
return False
- Используй тернарный оператор:
def describe_age(age):
if age < 18:
return "ребёнок"
else:
return "взрослый"
# Перепиши в одну строку через тернарный
- Применяй walrus в чтении ввода:
# Прочитать ввод пока не введут пустую строку
while True:
line = input("> ")
if not line:
break
print(f"Получили: {line}")
# Перепиши через walrus в одну условную проверку
Что дальше
- PHP - Условия и сравнение: == vs === - аналогичные конструкции в PHP с нюансами нестрогого сравнения и match
Освоили условия. В следующем уроке - циклы for и while: range/enumerate/zip, break/continue, идиома for-else, типичные ошибки.