Множественное ветвление: if-elif-else. Оператор match в Python
Ранее мы рассмотрели работу условного оператора if
. С помощью его расширенной версии if-else можно реализовать две отдельные ветви выполнения. Однако алгоритм программы может предполагать выбор больше, чем из двух путей, например, из трех, четырех или даже пяти. В данном случае следует говорить о необходимости множественного ветвления.
Рассмотрим конкретный пример. Допустим, в зависимости от возраста пользователя, ему рекомендуется определенный видеоконтент. При этом выделяют группы от 3 до 6 лет, от 6 до 12, от 12 до 16, 16+. Итого 4 диапазона. Как бы мы стали реализовывать задачу, имея в наборе инструментов только конструкцию if-else?
Самый простой ответ – последовательно проверять вхождение введенного числа-возраста в определенный диапазон с помощью следующих друг за другом условных операторов:
old = int(input('Ваш возраст: ')) print('Рекомендовано:', end=' ') if 3 <= old < 6: print('"Заяц в лабиринте"') if 6 <= old < 12: print('"Марсианин"') if 12 <= old < 16: print('"Загадочный остров"') if 16 <= old: print('"Поток сознания"')
Примечание. Названия фильмов выводятся на экран в двойных кавычках. Поэтому в программе для определения строк используются одинарные.
Предложенный код прекрасно работает, но есть одно существенное "но". Он не эффективен, так как каждый if
в нем – это отдельно взятый оператор, никак не связанный с другими if
. Процессор тратит время и "нервы" на обработку каждого из них, даже если в этом уже нет необходимости. Например, введено число 10. В первом if
логическое выражение возвращает ложь, и поток выполнения переходит ко второму if
. Логическое выражение в его заголовке возвращает истину, и его тело выполняется. Всё, на этом программа должна была остановиться.
Однако следующий if
никак не связан с предыдущим, поэтому далее будет проверяться вхождение значения переменной old в диапазон от 12 до 16, в чем необходимости нет. И далее будет обрабатываться логическое выражение в последнем if
, хотя уже понятно, что и там будет False
.
Решить проблему избыточности проверок можно, вкладывая условные операторы друг в друга:
old = int(input('Ваш возраст: ')) print('Рекомендовано:', end=' ') if 3 <= old < 6: print('"Заяц в лабиринте"') else: if 6 <= old < 12: print('"Марсианин"') else: if 12 <= old < 16: print('"Загадочный остров"') else: if 16 <= old: print('"Поток сознания"')
Рассмотрим поток выполнения этого варианта кода. Сначала проверяется условие в первом if
(он же самый внешний). Если здесь было получено True
, то тело этого if
выполняется, а в ветку else
мы даже не заходим, так как она срабатывает только тогда, когда в условии if
возникает ложь.
Если внешний if
вернул False
, поток выполнения программы заходит в соответствующий ему внешний else
. В его теле находится другой if
со своим else
. Если введенное число попадает в диапазон от 6 до 12, то выполнится тело вложенного if
, после чего программа завершается. Если же число не попадает в диапазон от 6 до 12, то произойдет переход к ветке else
. В ее теле находится свой условный оператор, имеющий уже третий уровень вложенности.
Таким образом до последней проверки (16 <= old
) интерпретатор доходит только тогда, когда все предыдущие возвращают False
. Если же по ходу выполнения программы возникает True
, то все последующие проверки опускаются, что экономит ресурсы процессора. Кроме того, такая логика выполнения программы более правильная.
Теперь зададимся следующим вопросом. Можно ли как-то оптимизировать код множественного ветвления и не строить лестницу из вложенных друг в друга условных операторов? Во многих языках программирования, где отступы используются только для удобства чтения программистом, но не имеют никакого синтаксического значения, часто используется подобный стиль:
if логическое_выражение { … ; } else if логическое_выражение { … ; } else if логическое_выражение { … ; } else { … ; }
Может показаться, что имеется только один уровень вложенности, и появляется новое расширение для if
, выглядящее как else if
. Но это только кажется. На самом деле if
, стоящее сразу после else
, является вложенным в это else
. Выше приведенная схема – то же самое, что
if логическое_выражение { … ; } else if логическое_выражение { … ; } else if логическое_выражение { … ; } else { … ; }
Именно так ее "понимает" интерпретатор или компилятор. Однако считается, что человеку проще воспринимать первый вариант.
В Питоне такое поднятие вложенного if
к внешнему else
невозможно, потому что здесь отступы и переходы на новую строку имеют синтаксическое значение. Поэтому в язык Python встроена возможность настоящего множественного ветвления на одном уровне вложенности, которое реализуется с помощью веток elif.
Слово "elif" образовано от двух первых букв слова "else", к которым присоединено слово "if". Это можно перевести как "иначе если".
В отличие от else
, в заголовке elif обязательно должно быть логическое выражение также, как в заголовке if
. Перепишем нашу программу, используя конструкцию множественного ветвления:
old = int(input('Ваш возраст: ')) print('Рекомендовано:', end=' ') if 3 <= old < 6: print('"Заяц в лабиринте"') elif 6 <= old < 12: print('"Марсианин"') elif 12 <= old < 16: print('"Загадочный остров"') elif 16 <= old: print('"Поток сознания"')
Обратите внимание, в конце, после всех elif
, может использоваться одна ветка else
для обработки случаев, не попавших в условия ветки if
и всех elif
. Блок-схему полной конструкции if-elif-…-elif-else можно изобразить так:
Как только тело if
или какого-нибудь elif
выполняется, программа сразу же возвращается в основную ветку (нижний ярко-голубой прямоугольник), а все нижеследующие elif
, а также else
пропускаются.
Оператор match-case в Python
Начиная с версии 3.10 в Питоне появился оператор match
, который можно использовать как аналог оператора switch
, который есть в других языках. На самом деле возможности match
немного шире.
В match
множественное ветвление организуется с помощью веток case
:
match имя_переменной: case значение_1: действия case значение_2: действия …
Слова match-case можно перевести как "соответствовать случаю". То есть, если значение переменной или выражения при match
соответствует значению при каком-либо case
, то выполнятся действия, вложенные в этот case
.
В отличие от if-elif здесь нельзя использовать логические выражения. После case
должен находится литерал, конкретное значение, выражение, возвращающее однозначный результат.
Рассмотрим программу, в которой реализовать множественное ветвление с помощью match-case удобнее, чем через if-elif-else:
sign = input('Знак операции: ') a = int(input('Число 1: ')) b = int(input('Число 2: ')) match sign: case '+': print(a + b) case '-': print(a - b) case '/': if b != 0: print(round(a / b, 2)) case '*': print(a * b) case _: print('Неверный знак операции')
Здесь значение переменной sign проверяется не на вхождение в какой-либо диапазон, а на точное соответствие заданным строковым литералам. При этом в ветках case
уже не надо писать sign == '+'
или sign == '-'
, как это пришлось бы делать в программе с if-elif:
if sign == '+': print(a + b) elif sign == '-': print(a - b) elif sign == '/': if b != 0: print(round(a / b, 2)) elif sign == '*': print(a * b) else: print('Неверный знак операции')
Код с match
выглядит более ясным.
Оператор match
языка Python не имеет ветки else
. Вместо нее используется ветка case _
.
При одном case
через оператор |
можно перечислять несколько значений. Если значение переменной соответствует хотя бы одному из них, тело этого case
выполнится.
sign = input('Знак операции: ') match sign: case '+' | '-' | '*': a = int(input('Число 1: ')) b = int(input('Число 2: ')) print(eval(f'{a} {sign} {b}')) case _: print('Неверный знак операции')
В коде выше с помощью функции eval()
переданная ей строка выполняется как выражение. Например, если были введены числа 3, 5 и знак *, то получится строка "3 * 5"
. Вызов eval("3 * 5")
возвращает число 15.
Практическая работа
-
Спишите вариант кода программы "про возраст" c
if
и тремя веткамиelif
из урока. Дополните его веткойelse
, обрабатывающие случаи, когда пользователь вводит числа не входящие в заданные четыре диапазона. Подумайте, почему в первой версии программы (когда использовались не связанные друг с другом условные операторы) нельзя было использоватьelse
, а для обработки не входящих в диапазоны случаев пришлось бы писать еще одинif
? -
Усовершенствуйте предыдущую программу, обработав исключение
ValueError
, возникающее, когда вводится не целое число. -
Напишите программу, которая запрашивает на ввод число. Если оно положительное, то на экран выводится цифра 1. Если число отрицательное, выводится -1. Если введенное число – это 0, то на экран выводится 0. Используйте в коде условный оператор множественного ветвления.
Примеры решения и дополнительные уроки в pdf-версии курса