$ sudo teach IT
Модуль 3 · Условия · Урок 3.6

match-case

Когда одну и ту же переменную нужно сравнить сразу со многими вариантами — способ сделать это без длинной цепочки elif.

Теория + практика~25 минутНовичок

Вы уже умеете строить цепочки if — elif — elif — else. Этого достаточно для любой задачи, но если вариантов много — скажем, вы проверяете код ответа сервера или номер дня недели — такая цепочка растягивается на десяток строк и в ней легко потеряться: везде повторяется одна и та же переменная и слово elif.

В Python есть отдельная конструкция именно для такого случая: когда одно значение нужно сравнить сразу со списком вариантов. Она называется match-case. Она не заменяет if полностью — это скорее удобный частный инструмент для конкретной ситуации, и в этом уроке вы разберётесь, когда его выбирать, а когда оставаться на if-elif.

Важно про версию Python. Конструкция 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.
match day: — говорит Python: «дальше будем сравнивать значение day». После значения обязательно двоеточие, как после условия в if.
case 1:, case 2:, case 3: — конкретные варианты значения day. У каждого свой блок кода с отступом в 4 пробела, точно так же, как тело if.
case _: — последний вариант, символ подчёркивания означает «всё остальное». К нему вернёмся подробнее в следующем разделе.

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_ticketif
Проверяются диапазоны через сравнения (<, >=) без явного перечисления значенийif, либо match с guard

Оба варианта дают одинаковый результат, разница только в удобстве чтения. Компилятору Python всё равно, что вы выберете — выбирайте то, что легче читается именно в вашей ситуации.

Частые ошибки

match-case в старой версии Python

match day:
    case 1:
        print("Понедельник")
SyntaxError на Python 3.9 и старше — конструкции просто не существует до версии 3.10. Если ошибка появилась внезапно, первым делом проверьте версию Python командой python --version.

Забыли case _ там, где он нужен

grade = 2

match grade:
    case 5:
        result = "Отлично"
    case 4:
        result = "Хорошо"

print(result)
NameError: name 'result' is not defined — ни один case не подошёл под 2, и case _ нет, поэтому весь match ничего не сделал. Переменная result так и не появилась, а print на неё уже ссылается. Всегда добавляйте case _:, если не уверены, что перечислили все возможные значения.

Путают _ с обычной переменной

match code:
    case _:
        print("Код:", _)
Не ошибка, но ловушка: значение code в _ не сохраняется, поэтому _ внутри блока не будет равен проверяемому значению. Если оно нужно — замените _ на обычное имя переменной, например other.

Что важно запомнить

match-case работает с Python 3.10 и новее. На более старой версии это SyntaxError.
Структура: match значение:, а ниже несколько case вариант:. После значения и после каждого варианта — двоеточие, тело каждого case с отступом в 4 пробела.
Python проверяет case по порядку сверху вниз и останавливается на первом совпадении.
case _: — wildcard, ловит всё, что не подошло раньше. Значение в него не сохраняется.
| объединяет несколько значений в одном case: case "a" | "b": сработает и для "a", и для "b".
Guard-условие после 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.