2.2 Условные конструкции#

Синтаксис условий#

В своём минимальном варианте условные конструкции в Python имеют следующий синтаксис:

if Условие:
    Блок инструкций 1

Здесь мы видим, что в конце строки с инструкцией if и условием обязательно ставится :, обозначающее, что дальше будет блок из инструкций, которые выполнятся только в случае, если условие верно, в противном случае этот блок игнорируется и не исполняется. Специальных команд для определения начала и конца блока инструкций, относящегося к условию, нет. Блок определяется только отступами. Строка инструкций, следующая за if, должна быть с новым уровнем отступов (как правило, добавляется 4 пробела в начале строки), все последующие инструкции с тем же уровнем отступов будут относиться к этому же блоку. Как только количество отступов вернется к исходному, значит, блок инструкций закончился.

В качестве условий обычно используются выражения, которые возвращают значения True или False. Например, арифметические операции сравнения:

x = 10

# Можете раскомментировать следующую строку для проверки работы условия со своими значениями
# x = int(input())

if x >= 0:
    print("Условие было верно")
    print("Число положительное")
Условие было верно
Число положительное

После блока if при необходимости можно добавить блок Иначе (else), который будет срабатывать только в случае, если условие оказалось неверным - вернуло False. Синтаксис конструкции будет выглядеть следующим образом:

if Условие:
    Блок инструкций. Если условие верно
else:
    Блок инструкций. Если условие неверно
x = -10
# x = int(input())

if x >= 0:
    print("Условие было верно")
    print("Число положительное")
else:
    print("Условие было неверно")
    print("Число отрицательное")
Условие было неверно
Число отрицательное

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

x = -9
# x = int(input())

if x >= 0:
    if x % 2 == 0:
        print("x положительный и четный")
    else:
        print("x положительный и нечетный")
else:
    if x % 2 == 0:
        print("x отрицательный и четный")
    else:
        print("x отрицательный и нечетный")
x отрицательный и нечетный

Также стоит помнить, что чем больше вложенных конструкций мы используем, тем сложнее воспринимать код. Нужно стараться писать код максимально просто, облегчая его читаемость. В данном случае, чтобы избежать вложенности и реализовать серию условий, можно использовать ещё одно ключевое слово, которое может встречаться в условной конструкции - elif. Как и else, elif является необязательной частью условия, он также всегда следует после блока if и может повторяться сколько угодно раз:

if Условие:
    Блок инструкций. Если условие верно
elif Условие:
    Блок инструкций. Если текущее условие верно, а все условия выше нет. 
elif Условие:
    Блок инструкций. Если текущее условие верно, а все условия выше нет.
else:
    Блок инструкций. Если все условия выше неверны

Отметим, что как только срабатывает одно из условий, все остальные дальше не проверяются и, соответственно, не исполняются. else срабатывает только в случаях, если ни одно условие не было верным.

x = -9
# x = int(input())

# Для составления сложных условий можно использовать логическое И (and) и ИЛИ (or), 
# а также скобки для определения порядка операций

if x >= 0 and x % 2 == 0:  
    print("x положительный и четный")
elif x >= 0 and x % 2 != 0:
    print("x положительный и нечетный")
elif x < 0 and x % 2 == 0:
    print("x отрицательный и четный")
else:
    print("x отрицательный и нечетный")
x отрицательный и нечетный

Falsy и Truthy значения#

Помимо выражения, возвращающего логический тип данных (True/False), в качестве условия можно указать абсолютно любой объект. В таком случае будет выполнено автоматическое приведение типа объекта к логическому. На практике это часто используется для проверки, не пустая ли переменная.

Значения, которые при приведении к логическому типу дают False, называют falsy (ложными), а все остальные — truthy (истинными).

Полный список falsy-значений довольно короткий. На данном этапе из уже знакомых нам типов к ложным относятся:

print(bool(False))       # False - логический тип
print(bool(0))           # 0 - целое число
print(bool(0.0))         # 0.0 - дробное число
print(bool(""))          # пустая строка
False
False
False
False

Также к этому списку будут относиться пустые коллекции, с которыми мы познакомимся чуть позже, и специальное значение None, которое обозначает отсутствие какого-либо значения.

Про все остальные значения мы можем сказать, что они являются truthy: непустые строки, непустые коллекции, любые ненулевые числа и т.д.

print(bool(1))           # True
print(bool(-1))          # True (отрицательные числа тоже truthy!)
print(bool("0"))         # True (строка "0" - непустая!)
print(bool("False"))     # True (строка "False" - непустая!)
True
True
True
True

На практике это позволяет писать более лаконичный код:

# Вместо такой проверки:
city_name = "Moscow"

if len(city_name) > 0:
    print("Название города указано")

# Можно написать проще:
if city_name:
    print("Название города указано")
Название города указано
Название города указано

Вычисление с коротким замыканием#

Теперь, когда мы понимаем, что любое значение может быть условием, посмотрим, как обрабатываются составные выражения из нескольких значений. Мы уже знакомились с логическими операторами and, or и not, которые позволяют комбинировать условия, но не всегда они вычисляются полностью. Есть такой концепт как — short-circuit evaluation (вычисление с коротким замыканием). Сейчас нам не столько важно запомнить правильное название этого концепта, сколько важно понять, как он работает. Суть в том, что когда результат уже определён по первой части выражения, вторая часть просто не будет выполняться:

  • A and B — если A ложно, то и всё выражение ложно. B не выполняется.

  • A or B — если A истинно, то и всё выражение истинно. B не выполняется.

# Вторая часть не будет выполняться, потому что первая уже False
False and print("Это не напечатается")

# Вторая часть не будет выполняться, потому что первая уже True
True or print("Это тоже не напечатается")
True

На этом поведении будут строиться полезные паттерны, которые мы будем упоминать далее. Пока, как пример, можно привести получение значения по умолчанию через or:

# Если первое значение falsy (None, пустая строка и т.д.), берётся второе
city_name = ""
display_name = city_name or "Город не указан"
print(display_name)  # "Город не указан"
Город не указан

Цепочки сравнений#

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

x = 55.75  # широта Москвы

# Вместо двух условий с and:
if x >= 50 and x <= 60:
    print("В диапазоне")

# Можно написать цепочку сравнений:
if 50 <= x <= 60:
    print("В диапазоне")
В диапазоне
В диапазоне

Так, например, цепочки сравнений могут оказаться полезными при работе с координатами, когда нужно проверить, что точка находится в пределах определённого охвата (bounding box):

lat = 55.75
lon = 37.62

# Проверка, что точка находится в пределах Московской области (примерно)
if 54.5 <= lat <= 56.5 and 36.5 <= lon <= 39.0:
    print("Точка в пределах Московской области")
Точка в пределах Московской области

Тернарный оператор#

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

Но на практике часто встречается другая задача: нужно не выполнить разные блоки кода, а просто выбрать одно из двух значений и присвоить его переменной. Например, определить текстовую метку, подставить значение по умолчанию или выбрать формат вывода.

Для таких случаев писать полноценный if/else с двумя ветками присваивания — избыточно и небезопасно: в одной из веток можно забыть присвоить переменную. Именно эту проблему решают тернарные операторы, также называемые условием в одну строку.

Рассмотрим пример. Допустим, мы определяем четность числа и с помощью обычного условия можем сделать это следующим образом:

x = 10
# x = int(input())

if x % 2 == 0:
    x_parity = "четный"
else:
    x_parity = "нечетный"

При использовании обычного условия для определения значения переменной код получается не очень безопасным. В одной из веток условной конструкции можно забыть определить имя переменной, что в свою очередь может привести к ошибке при дальнейшем исполнении кода. Тернарный оператор же решает данную проблему, гарантируя, что переменной всегда будет присвоено то или иное значение. Для его записи используется следующая конструкция:

Переменная = <Значение, если условие верно> if условие else <Значение, если условие неверно>

Что важно, в отличие от обычного условия блок else здесь является обязательным, а блока elif в принципе нет.

x = 10
# x = int(input())

x_parity = "четный" if x % 2 == 0 else "нечетный"

Сопоставление по шаблонам

В начале знакомства с Python сопоставления по шаблонам не найдут большого применения. Удобство их использования раскроется дальше, в создании алгоритмов, работа которых будет зависеть от количества параметров и типов данных переданных на вход. Поэтому на текущем этапе обучения имеет смысл пропустить данную управляющую конструкцию.

При возвращении к данному материалу позже с описанием конструкции и примерами её использования можно ознакомиться в PEP 622