Python 3.10で追加された「パターンマッチング(match-case)」入門!if-elif地獄から脱出!
Pythonをブラウザで実行しながら実践的に学ぶ
Pythonの基礎からソフトウェアアーキテクチャ,アルゴリズムなどの応用的な内容まで幅広く学べます。
ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。
プログラミングの学習を進めていくと、誰もが必ず一度はぶつかる壁があります。それは、条件分岐が多すぎてコードが読みにくくなってしまう現象です。
「もしAだったらこの処理、そうじゃなくてBだったらこの処理、Cだったら……」と、頭の中で考えているうちはシンプルに思えますよね。しかし、それをいざPythonのコードに落とし込もうとすると、画面が「if」と「elif」の文字で埋め尽くされてしまった経験はありませんか?
ちょっと条件が増えるたびに、elifの行をコピペして書き足していく。そのうちインデント(字下げ)も深くなり、どこからどこまでが一つの塊なのか、書いた本人でさえ分からなくなってしまう。これが、多くの初心者を苦しめる「if-elif地獄」の正体です。
あなたも、自分の書いたコードを見返して「なんだか不格好だな」「もっとスマートに書けないのかな」とため息をついたことがあるかもしれません。でも、安心してください。それはあなたの理解力が足りないからではなく、これまでのPythonに「複数の条件をスッキリと振り分ける専用の構文」が欠けていたからです。
実は、Python 3.10というバージョンから、この長年の悩みを解決する画期的な機能が追加されました。それが、今回ご紹介するパターンマッチング(match-case文)です。
この機能を使えば、まるで絡まった糸をスッとほどくように、複雑な条件分岐を驚くほど美しく、そして直感的に書き換えることができます。この記事では、プログラミング初心者の方でも絶対に理解できるように、具体的なサンプルコードを交えながら、match-case文の基本から実用的なテクニックまでをじっくりと解説していきます。
少しボリュームのある記事になりますが、読み終わる頃には、あなたのコーディングの引き出しが一つ増え、Pythonを書くのがもっと楽しくなっているはずです。
パターンマッチング(match-case)とは一体なにもの?¶
新しい文法と聞くと、「また難しいルールを覚えないといけないの?」と身構えてしまうかもしれません。
パターンマッチング(match-case文)は、一言で表すなら「超高性能な仕分けマシン」です。
箱の中にいろいろな形のブロックが流れてきたと想像してみてください。丸いブロックならAの箱へ、四角いブロックならBの箱へ、星型のブロックならCの箱へ……というように、流れてきたデータ(値や構造)の「形(パターン)」を見て、行き先を自動的に振り分けてくれる便利な機能、それがmatch-case文のイメージです。
他のプログラミング言語(例えばC言語やJavaなど)を少しでも触ったことがある方なら、「それって、要するにswitch文のことだよね?」と思うかもしれません。確かに、見た目や基本的な役割はswitch文によく似ています。
しかし、Pythonのmatch-case文は、他の言語のswitch文とは比べ物にならないほど強力です。ただ単に「値が一致するかどうか」を見るだけでなく、「データがどんな構造をしているか(リストなのか、辞書なのか、要素はいくつあるか)」までを細かくチェックして、柔軟に処理を分岐させることができるのです。
この「構造まで見抜ける」という点が、Pythonのパターンマッチングを「単なるswitch文のパクリ」ではなく、現代的な強力なツールに押し上げている最大の理由です。
なぜ「if-elif地獄」は生まれてしまうのか?¶
match-case文の素晴らしさを実感していただくために、まずは「なぜ私たちがこれほどまでにif-elif地獄に苦しんできたのか」を、実際のコードを見ながら振り返ってみましょう。
例えば、Webサイトを閲覧したときにサーバーから返ってくる「HTTPステータスコード」を判定して、画面にメッセージを表示するプログラムを作るとします。(404は「見つかりません」、500は「サーバーエラー」といったアレです)。
これまでのPythonの常識では、次のように書くしかありませんでした。
def check_status_old_way(status_code):
if status_code == 200:
return "OK: 成功しました!"
elif status_code == 400:
return "Bad Request: リクエストが不正です"
elif status_code == 403:
return "Forbidden: アクセス権限がありません"
elif status_code == 404:
return "Not Found: ページが見つかりません"
elif status_code == 500:
return "Internal Server Error: サーバーエラーが発生しました"
elif status_code == 502:
return "Bad Gateway: ゲートウェイエラーです"
elif status_code == 503:
return "Service Unavailable: サービスが利用できません"
else:
return f"Unknown status: 未知のステータスコード ({status_code}) です"
print(check_status_old_way(404))
どうでしょうか。決して間違ったコードではありませんし、正常に動作します。
しかし、status_code == という全く同じ文字が何度も何度も繰り返されていますよね。人間の目には、この繰り返される無駄な記述がノイズとなり、肝心な「どの数字のときに、どんなメッセージを返すのか」という本質的な情報がパッと頭に入ってきません。
綺麗なコードって何?初心者から一歩抜け出す「リーダブルコード」の3つの基本という記事でも解説していますが、良いコードとは「他の人が(あるいは未来の自分が)見たときに、一瞬で意図が伝わるコード」のことです。その観点からすると、このelifの連続は、お世辞にも美しいとは言えない状態なのです。
match-caseの基本的な書き方をマスターしよう¶
それでは、先ほどの不格好な「if-elif地獄」を、Python 3.10の救世主であるmatch-case文を使って書き換えてみましょう。
基本的な書き方はとてもシンプルです。まずは、判定したい対象(変数など)を match の後ろに置きます。そして、想定されるパターンを case の後ろに書いていき、それぞれの条件に合致したときの処理を記述するだけです。
百聞は一見に如かず。実際のコードを見てみましょう。
def check_status_new_way(status_code):
match status_code:
case 200:
return "OK: 成功しました!"
case 400:
return "Bad Request: リクエストが不正です"
case 403:
return "Forbidden: アクセス権限がありません"
case 404:
return "Not Found: ページが見つかりません"
case 500 | 502 | 503:
return "Server Error: サーバー側の問題が発生しています"
case _:
return f"Unknown status: 未知のステータスコード ({status_code}) です"
print(check_status_new_way(404))
いかがですか?驚くほどスッキリしましたよね!
status_code == という煩わしい繰り返しが消え去り、「どの値のときに何をするか」が縦に綺麗に並んで、直感的に読めるようになりました。これこそが、match-case文の最も基本的な恩恵です。
ここで、いくつか注目してほしいポイントがありますので、順番に解説していきます。
複数の条件をまとめる「| (パイプ)」の便利さ¶
上記のコードの case 500 | 502 | 503: の部分に注目してください。
これは、「ステータスコードが500、または502、または503のいずれかであれば、この処理を実行する」という意味になります。|(パイプライン記号)を使うことで、複数の条件を1行にまとめることができるのです。if文で status_code == 500 or status_code == 502 or status_code == 503 と長々と書いていた頃に比べると、天と地ほどの差がありますね。
どれにも当てはまらない場合の「_ (アンダースコア)」¶
if-elif文における else (どれにも当てはまらなかった場合)の役割を果たすのが、最後の case _: です。
この _ (アンダースコア)は、Pythonの世界ではよく「ワイルドカード(何にでも一致する万能カード)」や「使わない変数」を意味する記号として使われます。ここでは、「ここまでのcaseのどれにも一致しなかったら、とりあえずここに全部流してね」というキャッチオールの役割を果たしています。
Pythonのアンダーバー(アンダースコア)とはなんなのか?の記事でも触れていますが、Pythonにおいてアンダースコアは非常に多くの意味を持つ面白い記号です。
match-case文でも大活躍するので、ぜひこの使い方を覚えておきましょう。
単なるswitch文じゃない!match-caseの真のパワー¶
ここまで読んで、「なんだ、やっぱりただのswitch文じゃないか。ちょっとコードが短くなるだけか」と思ったあなた。ここからがPythonの真骨頂です。
冒頭で「パターンマッチングは構造まで見抜ける」とお話ししたのを覚えていますでしょうか。match-case文の本当の恐ろしさ(もちろん良い意味で)は、リストや辞書といった「複雑なデータ構造」をそのままの形でスパンッと判定できる点にあります。
順番に、その強力な機能を見ていきましょう。
リストやタプルの中身を展開して判定できる¶
例えば、ゲームを作っていて、プレイヤーの「コマンド」をリスト形式で受け取るとします。
コマンドは ["移動", "北"] のように「アクション名」と「方向」の2つの要素を持つこともあれば、["攻撃"] のように1つだけの場合もあるとしましょう。
if文を使ってこれを判定しようとすると、「リストの長さが1だったら…」「長さが2で、1番目の要素が『移動』だったら…」と、要素数(len())のチェックと中身の確認を同時に行わなければならず、コードが非常に複雑になります。
しかし、match-case文を使えば、データの「見た目の形」をそのまま条件に使うことができます。
def execute_command(command):
match command:
case ["攻撃"]:
print("剣で攻撃した!")
case ["移動", direction]:
print(f"{direction}の方向へ移動した!")
case ["魔法", magic_name, target]:
print(f"{target}に向かって{magic_name}を唱えた!")
case _:
print("そのコマンドは無効です。")
execute_command(["攻撃"]) # 出力: 剣で攻撃した!
execute_command(["移動", "北"]) # 出力: 北の方向へ移動した!
execute_command(["魔法", "ファイア", "ゴブリン"]) # 出力: ゴブリンに向かってファイアを唱えた!
このコードの美しさに気づきましたか?
case ["移動", direction]: という記述は、「もしリストの要素が2つで、1番目が『移動』だった場合、2番目の要素を変数 direction に代入して処理を進める」ということを、たった1行でやってのけているのです。
条件判定と変数の抽出(アンパック)を同時に行ってくれるため、驚くほど可読性の高いコードになります。
辞書(ディクショナリ)のキーと値も狙い撃ち¶
リストだけでなく、Pythonで最もよく使われるデータ型の一つである「辞書(ディクショナリ)」の判定も得意中の得意です。
Web APIなどからJSONデータを取得したとき、そのデータの中に特定のキーが存在するかどうか、そしてその値が何であるかを調べたいことはよくあります。
def process_user_data(user):
match user:
case {"name": name, "role": "admin"}:
print(f"管理者 {name} さん、ようこそ!全権限が付与されています。")
case {"name": name, "role": "user"}:
print(f"一般ユーザー {name} さん、こんにちは。")
case {"name": name}:
print(f"ゲストの {name} さん。ロールが設定されていません。")
case _:
print("無効なユーザーデータです。")
# さまざまなパターンの辞書データを渡してみます
user1 = {"name": "Alice", "role": "admin", "age": 30}
user2 = {"name": "Bob", "role": "user"}
user3 = {"name": "Charlie"}
process_user_data(user1)
process_user_data(user2)
process_user_data(user3)
ここでは、{"name": name, "role": "admin"} というパターンを定義しています。これは「辞書の中に"role"というキーがあって、その値が"admin"であり、かつ"name"というキーが存在すれば合致する」という意味です。同時に"name"の値を変数nameに代入しています。
驚くべきことに、user1のデータには条件に書いていない "age": 30 という要素が含まれていますが、match-case文は「指定したパターンがデータの中に含まれていればOK(部分一致)」と見なすため、エラーにならずに正しく処理されます。APIのレスポンスなど、不要なデータがたくさん混ざっている状況から「必要な情報だけをすくい取る」のに最適です。
if文を組み合わせた「ガード(Guard)」機能¶
「パターンの形だけじゃなくて、値の大きさ(大小比較)も条件に入れたいんだけど…」という場面もあるでしょう。例えば、RPGで「HPが20以下のときだけ逃げる」といった条件です。
そんなときは、case の後ろにさらに if を書き足すことができます。これを「ガード(Guard)」と呼びます。
def character_action(player):
match player:
case {"action": "逃げる", "hp": hp} if hp <= 20:
print("HPが少ない!無事に逃げ切った!")
case {"action": "逃げる", "hp": hp}:
print("まだ戦えるはずだ!逃げるのは失敗した。")
case {"action": "攻撃"}:
print("果敢に攻撃を仕掛けた!")
case _:
print("どうしていいかわからない。")
character_action({"action": "逃げる", "hp": 15}) # 出力: HPが少ない!無事に逃げ切った!
character_action({"action": "逃げる", "hp": 50}) # 出力: まだ戦えるはずだ!逃げるのは失敗した。
パターンマッチングによる構造の確認と、if文による数値の比較(論理条件)を組み合わせることで、どんなに複雑なビジネスロジックでも、スッキリと読みやすい形で表現できるようになります。
結局、if文とmatch-caseはどう使い分けるべき?¶
ここまでmatch-case文の便利さを熱く語ってきましたが、「じゃあ、これからは全部match-caseで書けばいいの?」というと、決してそうではありません。
if文にはif文の良さがあり、適材適所で使い分けることが、優秀なエンジニアへの第一歩です。これまでのif文が不要になったわけではないのです。
両者の特徴と、どのような場面でどちらを使うべきか、比較表を作って整理してみました。頭の整理に役立ててください。
| 比較項目 | if-elif文(従来の条件分岐) | match-case文(パターンマッチング) |
|---|---|---|
| 得意なこと | 「A > 10」や「文字列に"x"が含まれるか」など、純粋な真偽判定や大小比較 | 値の一致確認や、リスト・辞書などの「データ構造」の解析と抽出 |
| 可読性 | 条件が複雑になるとネストが深くなり、読みづらくなる | 縦に整然と並ぶため、条件が多くても視覚的にわかりやすい |
| 変数の抽出 | 条件判定とは別に、自分で変数に代入する処理を書く必要がある | パターンが一致した瞬間に、同時に変数への代入(抽出)を行える |
| 適している場面 | 条件が1〜2個しかないシンプルな分岐。複雑な論理演算(and/orの多用) | 3つ以上の複数の値による分岐。APIのJSON解析。コマンド処理 |
| 動作環境 | すべてのバージョンのPythonで動く | Python 3.10以降 が必須 |
基本的には、「単純な大小比較や、条件が少ない場合はif文」「特定の状態やデータ構造によって3つ以上の処理に枝分かれする場合はmatch-case文」という基準で選ぶと、きれいなコードに仕上がります。
例えば、Pythonのfor文やif文を1行で書く方法で解説したような、三項演算子を使った非常にシンプルな条件分岐を、わざわざmatch-case文で書くのは大げさすぎて逆に読みにくくなってしまいます。道具は使い所が肝心ですね。
match-caseを使う際の注意点¶
最後に、match-case文を実際の開発現場で使う際の重要な注意点を一つだけお伝えしておきます。
それは比較表にも記載した通り、「Python 3.10以上でしか動かない」ということです。
個人の学習や最新の環境で作るアプリであれば全く問題ありません。しかし、仕事で古いシステムを改修する場合や、AWSのLambdaなどで指定された古いPythonバージョンを使わなければならない環境でこの構文を使うと、【PythonのSyntaxErrorとは?】初心者がつまずく構文エラーの原因と対処法を徹底解説で紹介したような構文エラー(SyntaxError)が発生してシステムが止まってしまいます。
もしあなたが手元のパソコンで「match-case文を試したいのにエラーが出る!」という場合は、お使いのPythonのバージョンが古い可能性があります。pyenvの使い方をわかりやすく解説!Pythonのバージョンを変えるなどの記事を参考に、最新のPython環境を構築してからチャレンジしてみてくださいね。
まとめ:新しい文法を取り入れて、ワンランク上のPython使いへ¶
いかがだったでしょうか。長年Pythonプログラマーを悩ませてきた「if-elif地獄」から脱出するための強力な武器、match-case文について解説しました。
単なる「便利なswitch文」にとどまらず、リストや辞書の中身を解析しながら変数を抽出してくれるその機能は、まさに魔法のように便利ですよね。
プログラミング言語は、時代とともに進化し続けています。「昔覚えたやり方」だけでコードを書き続けることもできますが、今回紹介したような新しい文法を少しずつ取り入れていくことで、あなたの書くコードはより短く、より読みやすく、よりバグの少ない洗練されたものへと進化していきます。
次にあなたが「if-elif」を3つ以上書き並べてしまったときは、ぜひこの記事を思い出して、「そうだ、match-caseでスッキリ書けないかな?」と立ち止まって考えてみてください。その少しの工夫の積み重ねが、あなたを初心者から中級者、そして上級者へと引き上げてくれるはずです。
これからも、Pythonの便利で奥深い機能を一緒に楽しんで学んでいきましょう!