Вы уже умеете строить цепочки if — elif — elif — else. Этого достаточно для любой задачи, но если вариантов много — скажем, вы проверяете код ответа сервера или номер дня недели — такая цепочка растягивается на десяток строк и в ней легко потеряться: везде повторяется одна и та же переменная и слово elif.
В Python есть отдельная конструкция именно для такого случая: когда одно значение нужно сравнить сразу со списком вариантов. Она называется match-case. Она не заменяет if полностью — это скорее удобный частный инструмент для конкретной ситуации, и в этом уроке вы разберётесь, когда его выбирать, а когда оставаться на if-elif.
match-case появилась в Python 3.10 (октябрь 2021 года). Если у вас установлена более старая версия, программа с match не запустится — будет SyntaxError. Проверить версию можно в терминале командой python --version. На платформе для выполнения задач это уже учтено, а вот при запуске на своём компьютере версию стоит проверить.match и case — как устроена конструкция
Простыми словами: представьте почтовое отделение с несколькими окошками, на каждом из которых висит табличка с индексом. Вы подходите с письмом, смотрите на индекс и идёте к окошку с таким же номером. Если подходящего окошка нет — идёте к окошку «для остальных случаев». match-case делает то же самое с одним значением: берёт его и по очереди сверяет с табличками, пока не найдёт совпадение.
Официально эта конструкция называется оператор match (от английского «сопоставить»). Значение, которое проверяют, указывают после слова match, а каждый вариант — это отдельная строка case («случай»). Python в документации называет это сопоставлением с образцом: каждый case задаёт образец, а Python проверяет, подходит ли под него значение.
day = 3
match day:
case 1:
print("Понедельник")
case 2:
print("Вторник")
case 3:
print("Среда")
case _:
print("Другой день")
Разбираем построчно:
day = 3 — обычное присваивание: кладём в переменную day число 3.day». После значения обязательно двоеточие, как после условия в if.case 1:, case 2:, case 3: — конкретные варианты значения day. У каждого свой блок кода с отступом в 4 пробела, точно так же, как тело if.Python проверяет варианты по порядку сверху вниз и останавливается на первом, который совпал. Так как day равно 3, сработает третий case, остальные даже не проверяются — программа выведет Среда. Если бы вы забыли двоеточие после match day или после какого-то case, Python сразу остановился бы с SyntaxError, не запустив программу вовсе — ровно как в случае с if.
_ — вариант на все оставшиеся случаи
Простыми словами: это как строчка «в остальных случаях» в инструкции. Вы не можете перечислить вообще все возможные значения — их может быть бесконечно много — поэтому в конце добавляют вариант, который ловит всё, что не подошло под предыдущие.
Официально символ подчёркивания _ в паттерне называется wildcard-паттерн (от английского «джокер», символ, заменяющий любое значение). Он играет ту же роль, что else в конструкции if-else: срабатывает, только если ни один case выше не подошёл. Если его не написать, а ни один вариант не совпал — ничего страшного не случится: программа просто пропустит весь match и пойдёт дальше, без ошибки.
status_code = 404
match status_code:
case 200:
print("Запрос выполнен успешно")
case 404:
print("Страница не найдена")
case _:
print("Код", status_code, "программа не знает")
case 200: и case 404: — два конкретных кода, которые программа умеет распознавать.case _: — сюда попадёт абсолютно любой код, кроме 200 и 404: и 403, и 500, и 999.print("Код", status_code, "программа не знает") — здесь print вызван с тремя аргументами через запятую. Вы уже видели, что print по умолчанию соединяет свои аргументы пробелом — поэтому переменная status_code просто встанет между двух строк.Так как status_code равно 404, сработает второй case, и программа выведет Страница не найдена.
_ — это не переменная. Значение в него не сохраняется, и после case _: обратиться к проверяемому значению по имени _ нельзя. Если внутри блока значение всё же нужно — используйте вместо _ обычное имя переменной, например case other:. Тогда всё, что не совпало раньше, попадёт в переменную other.| — несколько значений в одном case
Простыми словами: иногда несколько разных значений должны приводить к одному и тому же результату — суббота и воскресенье одинаково «выходной», а нажатие клавиш «y» и «Y» одинаково означает «да». Неудобно писать под каждое такое значение отдельный case с повторяющимся телом — хочется объединить их в одну строку.
Официально символ | (вертикальная черта, находится обычно на той же клавише, что и \) в паттерне называется OR-паттерн, потому что читается как «или». Он перечисляет несколько значений через | в одном case: блок выполнится, если проверяемое значение равно хотя бы одному из них.
answer = "y"
match answer:
case "y" | "yes" | "Y":
print("Подтверждено")
case "n" | "no" | "N":
print("Отклонено")
case _:
print("Не понял ответ")
case "y" | "yes" | "Y": — три строки перечислены через |. Блок ниже выполнится, если answer равен любой из них.case — три варианта «нет» в одной строке вместо трёх отдельных блоков с одинаковым print.Переменная answer равна "y", это первый вариант из списка в первом case — программа выведет Подтверждено. Без OR-паттерна пришлось бы писать шесть строк case вместо двух, и в каждой повторять один и тот же print.
Guard — дополнительная проверка внутри case
Простыми словами: иногда одного совпадения мало, хочется добавить ещё условие — «и только если число ещё и положительное». Для этого к case можно прицепить дополнительную проверку, которая сработает уже после того, как основное сравнение прошло.
Официально такая проверка называется guard-условие (от английского «страж»: как бы дополнительно охраняет вход в блок). Пишется после паттерна через ключевое слово if и обычное логическое выражение, которое вы разбирали раньше. Блок выполнится, только если совпали оба условия: и паттерн, и guard.
temperature = 35
match temperature:
case t if t < 0:
print("Мороз")
case t if t < 20:
print("Прохладно")
case t:
print("Тепло или жарко")
case t if t < 0: — здесь t не литеральное значение, а обычное имя переменной. Такой паттерн ловит вообще любое значение temperature и одновременно кладёт его в t — а дальше проверяется guard-условие t < 0. Блок сработает, только если и значение поймалось, и t действительно меньше нуля.case устроен так же, только guard t < 20.case t: в самом конце — без guard, поэтому ловит абсолютно всё, что осталось. По смыслу это то же самое, что case _:, только значение ещё и сохраняется в t на случай, если оно понадобится внутри блока.temperature равно 35: первый guard 35 < 0 даёт False, второй 35 < 20 тоже даёт False, поэтому обе первые ветки пропускаются и срабатывает последняя — программа выведет Тепло или жарко.
_: символ _ ничего не сохраняет, а обычное имя переменной, как t, — сохраняет. Если внутри блока значение не понадобится, пишите _, если понадобится — давайте ему имя.match или if-elif — что выбрать
match-case не отменяет всё, что вы уже знаете про if-elif-else. Это два разных инструмента для разных ситуаций, и полезно сразу понимать границу между ними.
| Ситуация | Что выбрать |
|---|---|
| Одна переменная сравнивается с несколькими фиксированными значениями | match |
Условие составное, сразу несколько разных переменных: age >= 18 and has_ticket | if |
Проверяются диапазоны через сравнения (<, >=) без явного перечисления значений | if, либо match с guard |
Оба варианта дают одинаковый результат, разница только в удобстве чтения. Компилятору Python всё равно, что вы выберете — выбирайте то, что легче читается именно в вашей ситуации.
Частые ошибки
match-case в старой версии Python
match day:
case 1:
print("Понедельник")
python --version.Забыли case _ там, где он нужен
grade = 2
match grade:
case 5:
result = "Отлично"
case 4:
result = "Хорошо"
print(result)
case не подошёл под 2, и case _ нет, поэтому весь match ничего не сделал. Переменная result так и не появилась, а print на неё уже ссылается. Всегда добавляйте case _:, если не уверены, что перечислили все возможные значения.Путают _ с обычной переменной
match code:
case _:
print("Код:", _)
code в _ не сохраняется, поэтому _ внутри блока не будет равен проверяемому значению. Если оно нужно — замените _ на обычное имя переменной, например other.Что важно запомнить
match-case работает с Python 3.10 и новее. На более старой версии это SyntaxError.match значение:, а ниже несколько case вариант:. После значения и после каждого варианта — двоеточие, тело каждого case с отступом в 4 пробела.case по порядку сверху вниз и останавливается на первом совпадении.case _: — wildcard, ловит всё, что не подошло раньше. Значение в него не сохраняется.| объединяет несколько значений в одном case: case "a" | "b": сработает и для "a", и для "b".if в case добавляет дополнительную проверку: блок сработает, только если совпали и паттерн, и это условие.match, когда одна переменная сравнивается со списком фиксированных значений. Для составных условий с несколькими переменными оставайтесь на if.Проверьте себя
4 вопроса
Дни недели
Дана переменная day = "воскресенье". Используйте match-case, чтобы присвоить переменной day_type:
"Рабочий день"для"понедельник" | "вторник" | "среда" | "четверг" | "пятница""Выходной"для"суббота" | "воскресенье""Неизвестно"для всех остальных случаев
Выведите day_type.
HTTP статусы
Дана переменная status_code = 404. Используйте match-case, чтобы присвоить переменной message:
"Успех"для200 | 201 | 204"Перенаправление"для301 | 302"Ошибка клиента"для400 | 401 | 403 | 404"Ошибка сервера"для500 | 502 | 503"Неизвестный код"для всех остальных
Выведите message.
Калькулятор через match
Даны переменные a = 12, b = 4 и op = "*" — знак операции. Используйте match-case, чтобы вычислить result:
"+"— сложение"-"— вычитание"*"— умножение"/"— деление; еслиbравен 0, вместо результата запишите вresultстроку"Ошибка: деление на ноль"- для любого другого знака запишите в
resultстроку"Неизвестная операция"
Выведите result.