Python & AI Tutorials Logo
Python プログラミング

13. match と case で分岐する(構造的パターンマッチング)

プログラムが複数の可能な値やパターンに基づいて判断する必要があるとき、あなたはすでに第8章で if-elif-else チェーンを使う方法を学んでいます。Python 3.10 では、matchcase 文を使う 構造的パターンマッチング(structural pattern matching) という強力な代替手段が導入されました。この機能は、複雑な意思決定シナリオを扱うための、よりクリーンで表現力の高い方法を提供します。

パターンマッチング(pattern matching)は単純な値比較を超えています。データの構造や形に対してマッチさせたり、複雑なオブジェクトから値を抽出したり、複数分岐をより読みやすい形で表現したりできます。if-elif-else チェーンは多くの状況で十分に機能しますが、match-case 文は、複数のはっきり分かれたケースを扱うとき、特に構造化データを扱うときに真価を発揮します。

13.1) match と case 文の導入(第8章の if-elif を土台に)

13.1.1) match-case の基本構造

match 文は値(subject(サブジェクト) と呼ばれます)を調べ、case 節で定義された1つ以上の パターン(pattern) と比較します。パターンが一致すると、Python はその case に対応するコードブロックを実行します。

基本構造は次のとおりです。

python
match subject:
    case pattern1:
        # pattern1 が一致した場合に実行するコード
    case pattern2:
        # pattern2 が一致した場合に実行するコード
    case pattern3:
        # pattern3 が一致した場合に実行するコード

基本概念を示す簡単な例から始めましょう。

python
# シンプルな HTTP ステータスコードハンドラ
status_code = 404
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")

Output:

Error: Page not found

この例では、match 文が status_code(サブジェクト)を調べます。Python は各 case パターンを順番にチェックします。status_code404 に等しいことを見つけると、対応するコードブロックを実行してから match 文を抜けます。残りの case はチェックされません。

13.1.2) match-case と if-elif-else の違い

「これって if-elif-else で書けるのでは?」と思うかもしれません。はい、書けます。

python
status_code = 404
 
if status_code == 200:
    print("Success: Request completed")
elif status_code == 404:
    print("Error: Page not found")
elif status_code == 500:
    print("Error: Server error")

Output:

Error: Page not found

どちらのバージョンも同じ結果になります。しかし、match-case にはいくつかの利点があります。

  1. 意図がより明確: match 文は、1つの値を複数の可能性と照合していることを明示的に示します
  2. 繰り返しが少ない: すべての比較で変数名を繰り返し書く必要がありません
  3. より強力なパターン: これから見ていくように、match-case は単純な等価チェック以上のことができます
  4. 可読性が高い: 複雑な分岐ツリーでは、match-case の方が理解しやすいことがよくあります

13.1.3) どのパターンにも一致しない場合

どのパターンにも一致しなかったらどうなるでしょうか。match 文は、どの case ブロックも実行せずにそのまま終了します。

python
# ユーザーロールチェッカー
user_role = "guest"
 
match user_role:
    case "admin":
        print("Full system access granted")
    case "moderator":
        print("Content management access granted")
    case "editor":
        print("Editing access granted")
 
print("Role check complete")

Output:

Role check complete

"guest" はどのパターンにも一致しないため、どの case ブロックも実行されません。プログラムは match 文の後のコードへ進みます。この挙動は理解しておくことが重要です。if-elif-else チェーンでは最後に else 節を追加してそれ以外すべてのケースを捕捉できますが、基本的な match 文に「すべてを受ける」パターンがない場合、どのパターンにも一致しなければ静かに何もしません。

13.1.4) 実用例:メニュー選択システム

ユーザーの選択を扱う際に match-case がどれだけ明快かを示す、もう少し完成度の高い例を作ってみましょう。

python
# レストラン注文システム
menu_choice = 3
 
match menu_choice:
    case 1:
        item = "Caesar Salad"
        price = 8.99
        print(f"You ordered: {item} - ${price}")
    case 2:
        item = "Grilled Chicken"
        price = 14.99
        print(f"You ordered: {item} - ${price}")
    case 3:
        item = "Vegetable Pasta"
        price = 12.99
        print(f"You ordered: {item} - ${price}")
    case 4:
        item = "Chocolate Cake"
        price = 6.99
        print(f"You ordered: {item} - ${price}")
 
print("Order submitted to kitchen")

Output:

You ordered: Vegetable Pasta - $12.99
Order submitted to kitchen

この例は、各 case に複数の文を含められることを示しています。menu_choice3 に一致すると、Python はその case ブロック内の3行すべて(item の代入、price の代入、注文確認の出力)を実行します。

13.2) _ ワイルドカード、リテラルパターン、複数パターンの使用

13.2.1) ワイルドカードパターン:それ以外すべてを捕捉する

アンダースコア _ は、何にでも一致する特別なパターンです。通常は最後の case として使い、それまでのパターンに一致しなかったすべての値を扱います。これは if-elif-else チェーンの最後の else 節に似ています。

python
# デフォルト case 付き HTTP ステータスコードハンドラ
status_code = 403
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")
    case _:
        print(f"Unhandled status code: {status_code}")

Output:

Unhandled status code: 403

_ パターンはキャッチオール(catch-all)として働きます。403 は特定の case のどれにも一致しないため、ワイルドカードパターンが一致してそのブロックを実行します。ワイルドカードパターンはどんな値にも一致してしまうので、必ず最後に置くべきです。これより後ろに case があっても、決して実行されません。

ワイルドカードが実務で役立つ理由は次の例で分かります。

python
# 曜日スケジューラ
day = "Saturday"
 
match day:
    case "Monday":
        print("Team meeting at 9 AM")
    case "Wednesday":
        print("Project review at 2 PM")
    case "Friday":
        print("Weekly report due")
    case _:
        print(f"{day}: No scheduled events")

Output:

Saturday: No scheduled events

ワイルドカードパターンがないと、day"Saturday""Sunday" などの値だった場合、match 文は何も出力せずに静かに終了します。ワイルドカードを使えば、想定外または未指定のケースも適切に扱えます。

13.2.2) リテラルパターン:特定の値に一致させる

リテラルパターン(literal patterns) は、完全に一致する値にマッチします。これまで使ってきた数値・文字列・真偽値はすべてリテラルパターンです。

python
# 信号機コントローラ
light_color = "yellow"
 
match light_color:
    case "green":
        print("Go")
    case "yellow":
        print("Caution: Light changing soon")
    case "red":
        print("Stop")
    case _:
        print("Invalid light color")

Output:

Caution: Light changing soon

異なる型のリテラルパターンを使うこともでき、match は値と型の両方を比較します。

python
# 設定バリデータ(異なるリテラル型を使用)
setting_value = True
 
match setting_value:
    case True:        # 真偽値リテラル
        print("Feature enabled")
    case False:       # 真偽値リテラル
        print("Feature disabled")
    case None:        # None リテラル
        print("Feature not configured")
    case 0:           # 整数リテラル
        print("Feature explicitly turned off")
    case "auto":      # 文字列リテラル
        print("Feature set to automatic mode")
    case _:
        print("Invalid configuration value")

Output:

Feature enabled

リテラルパターンは整数、浮動小数点数、文字列、真偽値、None で使えます。Python は == 演算子と同じルールで等価性をチェックします。

13.2.3) OR 演算子による複数パターン

複数の異なる値に対して同じコードを実行したいことがあります。|(パイプ)演算子を使うと複数のパターンを組み合わせられ、意味は「または(or)」です。

python
# 週末判定
day = "Saturday"
 
match day:
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
        print("It's a weekday - time to work!")
    case "Saturday" | "Sunday":
        print("It's the weekend - time to relax!")
    case _:
        print("Invalid day name")

Output:

It's the weekend - time to relax!

| 演算子を使うと、同じアクションを起動する複数のパターンを指定できます。サブジェクトが | で区切られたパターンのいずれかに一致した場合、その case が実行されます。これは、同一コードブロックを持つ case を別々に書くよりずっとすっきりしています。

| と一緒に異なる種類のパターンを混在させることもできます。

python
# yes/no 質問の入力バリデータ
response = "yes"
 
match response:
    case True | "yes":
        print("You confirmed the action")
    case False | "no":
        print("You cancelled the action")
    case _:
        print("Please answer yes or no")

Output:

You confirmed the action

13.2.4) as でどの選択肢が一致したかを捕捉する

| で複数パターンを使うとき、どの値が具体的に一致したのかを知りたい場合があります。as キーワードを使うと、一致した値を捕捉できます。

python
# グループ化した応答を持つステータスコードハンドラ
status = 201
 
match status:
    case 200 | 201 | 202 | 204 as success_code:
        print(f"Success: {success_code}")
    case 400 | 401 | 403 | 404 as client_error:
        print(f"Client error: {client_error}")
    case 500 | 502 | 503 as server_error:
        print(f"Server error: {server_error}")
    case _:
        print("Unknown status code")

Output:

Success: 201

as キーワードは、どの選択肢が一致したかを捕捉する束縛変数(binding variable)を作ります。この例では、success_code200 | 201 | 202 | 204 の選択肢のうち実際に一致した 201 に束縛されます。

ログ出力で役立つ別の例も見てみましょう。

python
# ログレベル処理
log_level = "WARN"
 
match log_level:
    case "DEBUG" | "TRACE" as level:
        print(f"Verbose logging: {level}")
        print("Detailed diagnostic information will be recorded")
    case "INFO" | "NOTICE" as level:
        print(f"Informational: {level}")
        print("Normal operation messages will be recorded")
    case "WARN" | "WARNING" as level:
        print(f"Warning level: {level}")
        print("Potential issues detected")
    case "ERROR" | "FATAL" | "CRITICAL" as level:
        print(f"Error level: {level}")
        print("Immediate attention required")
    case _:
        print("Unknown log level")

Output:

Warning level: WARN
Potential issues detected

13.3) 束縛変数で値を抽出する

13.3.1) 束縛変数とは?

ここまではリテラル値に対してマッチさせてきました。しかし、パターンマッチング(pattern matching)は、マッチ対象のデータの一部を 捕捉(capture) または 抽出(extract) できるようになると、本当に強力になります。束縛変数(binding variable)キャプチャパターン(capture pattern) とも呼ばれます)は、パターン内の名前で、マッチした値を捕捉して case ブロック内で使えるようにします。

簡単な例を示します。

python
# シンプルな値の捕捉
command = "save"
 
match command:
    case "quit":
        print("Exiting program")
    case action:  # これは束縛変数です
        print(f"Executing action: {action}")

Output:

Executing action: save

パターン action は束縛変数です。これはワイルドカード _ のようにどんな値にも一致しますが、_ と違って、その値を捕捉して action という名前に代入します。case ブロック内では、action を使って一致した値を参照できます。

重要な違い: 束縛変数は _ と同じように 何にでも 一致します。違いは、_ は値を捨てるのに対し、束縛変数は case ブロックで使うために値を捕捉することです。

13.3.2) 束縛変数とワイルドカードの比較

束縛変数とワイルドカードを直接比較してみましょう。

python
# ワイルドカードを使用 - 値は捕捉されない
status = 403
 
match status:
    case 200:
        print("Success")
    case _:
        print("Some other status code")  # 実際の値にはアクセスできません

Output:

Some other status code

次に束縛変数を使った場合です。

python
# 束縛変数を使用 - 値が捕捉される
status = 403
 
match status:
    case 200:
        print("Success")
    case code:  # 束縛変数が値を捕捉します
        print(f"Status code {code} received")

Output:

Status code 403 received

束縛変数 code403 を捕捉するため、case ブロック内で利用できます。これは、特定のパターンに一致しなかった実際の値を扱う必要があるときに便利です。

13.3.3) タプルパターンにマッチして要素を抽出する

パターンマッチング(pattern matching)は、タプルのような構造化データで特に強力になります。タプルの形にマッチさせつつ、同時に要素を抽出できます。タプルは第15章で詳しく学びますが、この例では match 文でタプルパターンがどのように動くかにだけ焦点を当てます。

python
# 座標系 - タプルパターンにマッチ
point = (3, 7)
 
match point:
    case (0, 0):
        print("Origin point")
    case (0, y):  # y 軸上の任意の点にマッチ
        print(f"On y-axis at y={y}")
    case (x, 0):  # x 軸上の任意の点にマッチ
        print(f"On x-axis at x={x}")
    case (x, y):  # それ以外の任意の点にマッチ
        print(f"Point at coordinates ({x}, {y})")

Output:

Point at coordinates (3, 7)

何が起きているのかを分解してみましょう。

  1. サブジェクト point はタプル (3, 7) です
  2. Python は各 case パターンを順番にチェックします
  3. 最初の3つのパターンは、特定の位置に値 0 が必要ですが、タプル (3, 7) には 0 に等しい要素がないため一致しません
  4. パターン (x, y) は2要素のタプルなので一致します
  5. Python は x3 に、y7 に束縛します
  6. 捕捉した値を使って case ブロックが実行されます

別のタプルパターンの例も見てみましょう。

python
# RGB カラー解析
color = (255, 0, 0)
 
match color:
    case (0, 0, 0):
        print("Black")
    case (255, 255, 255):
        print("White")
    case (r, 0, 0):  # 強度が変化する純赤
        print(f"Pure red with intensity {r}")
    case (0, g, 0):  # 純緑
        print(f"Pure green with intensity {g}")
    case (0, 0, b):  # 純青
        print(f"Pure blue with intensity {b}")
    case (r, g, b):  # それ以外の色
        print(f"RGB color: red={r}, green={g}, blue={b}")

Output:

Pure red with intensity 255

この入力はパターン (r, 0, 0) に一致します。タプルが3要素で、最後の2つが 0 であり、先頭の値が r に束縛されるためです。

13.3.4) リストパターンにマッチする

リストパターンにもマッチさせて要素を抽出できます。リストは第14章で詳しく扱いますが、ここでは match 文でリストパターンがどう動くかに焦点を当てます。

python
# 引数付きコマンド
command = ["move", "north", "5"]
 
match command:
    case ["quit"]:
        print("Exiting game")
    case ["look"]:
        print("You look around the room")
    case ["move", direction]:
        print(f"Moving {direction}")
    case ["move", direction, distance]:
        print(f"Moving {direction} for {distance} steps")
    case _:
        print("Unknown command")

Output:

Moving north for 5 steps

パターン ["move", direction, distance] は、先頭要素が "move" である3要素のリストに一致します。2番目の要素を direction、3番目を distance として捕捉します。

リスト長が変化する実用例を示します。

python
# ショッピングカート項目の処理
item = ["laptop", 999.99, 2]
 
match item:
    case [name]:  # 名前だけの項目
        print(f"Item: {name} (no price or quantity specified)")
    case [name, price]:  # 名前と価格がある項目
        print(f"Item: {name}, Price: ${price}, Quantity: 1 (default)")
    case [name, price, quantity]:  # 項目情報が揃っている
        total = price * quantity
        print(f"Item: {name}, Price: ${price}, Quantity: {quantity}")
        print(f"Subtotal: ${total}")
    case _:
        print("Invalid item format")

Output:

Item: laptop, Price: $999.99, Quantity: 2
Subtotal: $1999.98

リストがちょうど3要素で、それぞれが対応する変数に束縛されるため、case [name, price, quantity] が実行されます。

13.3.5) 辞書パターンにマッチする

パターンマッチング(pattern matching)は辞書にも対応しており、特定のキーにマッチさせたり、その値を抽出したりできます。辞書は第16章で詳しく学びますが、この節では match 文で辞書パターンがどう動くかにだけ焦点を当てます。

python
# ユーザープロファイル処理
user = {"name": "Alice", "role": "admin", "active": True}
 
match user:
    case {"role": "admin", "active": True}:
        print("Active administrator - full access granted")
    case {"role": "admin", "active": False}:
        print("Inactive administrator - access suspended")
    case {"role": role, "active": True}:  # role の値を捕捉
        print(f"Active user with role: {role}")
    case {"role": role, "active": False}:
        print(f"Inactive user with role: {role}")
    case _:
        print("Invalid user profile")

Output:

Active administrator - full access granted

辞書パターンはキーと値のペアの一致が必要であり、この完全一致はより一般的なパターンより先にチェックされるため、case {"role": "admin", "active": True} が実行されます。

辞書パターンは柔軟で、指定したキーが指定した値で存在していれば一致します。辞書に追加のキーがあっても問題ありません。

python
# API 応答ハンドラ
response = {"status": "success", "data": {"id": 123, "name": "Product"}, "timestamp": "2025-12-17"}
 
match response:
    case {"status": "error", "message": msg}:
        print(f"Error occurred: {msg}")
    case {"status": "success", "data": data}:
        print(f"Success! Data received: {data}")
    case _:
        print("Unknown response format")

Output:

Success! Data received: {'id': 123, 'name': 'Product'}

辞書に追加の "timestamp" キーがあっても、パターン {"status": "success", "data": data} は一致します。このパターンは、指定したキーが指定した値(またはパターン)で存在することだけを要求します。

13.3.6) リテラルと束縛変数を組み合わせる

リテラルパターンと束縛変数を混ぜて、洗練されたマッチングロジックを作れます。
先ほどのタプル例は構造や位置に焦点を当てていましたが、この例ではリテラル値と束縛変数を組み合わせて実世界の判断ロジックを実装する方法を示します。

python
# HTTP リクエストルータ
request = ("GET", "/api/users", 42)
 
match request:
    case ("GET", "/", None):
        print("Homepage request")
    case ("GET", path, None):
        print(f"GET request for: {path}")
    case ("POST", path, data):
        print(f"POST request to {path} with data: {data}")
    case ("GET", path, user_id):
        print(f"GET request for {path} with user ID: {user_id}")
    case _:
        print("Unsupported request type")

Output:

GET request for /api/users with user ID: 42

この例は、"GET" のような特定値にマッチさせつつ、pathuser_id のような値を同じパターン内で捕捉できることを示しています。

13.3.7) 実用例:イベントハンドラ

束縛変数の力を示す完全な例を作ってみましょう。

python
# ゲームイベントハンドラ
event = ("player_move", {"x": 10, "y": 5, "speed": 2})
 
match event:
    case ("player_move", {"x": x, "y": y}):
        print(f"Player moved to position ({x}, {y})")
    case ("player_attack", {"target": target, "damage": damage}):
        print(f"Player attacked {target} for {damage} damage")
    case ("item_pickup", {"item": item_name}):
        print(f"Player picked up: {item_name}")
    case ("game_over", {"score": final_score}):
        print(f"Game ended. Final score: {final_score}")
    case (event_type, data):
        print(f"Unknown event type: {event_type}")
        print(f"Event data: {data}")

Output:

Player moved to position (10, 5)

このイベントハンドラは、イベント種別とイベントデータの辞書を含むタプルにマッチします。イベント種別に応じて辞書から特定の値を抽出するため、異なる種類のイベントをクリーンで読みやすいコードで処理できます。

13.4) if ガードで追加条件を付ける

13.4.1) ガードとは?

パターンにマッチさせた上で、さらに追加の条件もチェックしたいことがあります。if ガード(if guard) は、if キーワードを使って case パターンに追加できる条件です。パターンが一致し、かつガード条件が真(true)である場合にのみ、その case が一致します。

構文は次のとおりです。

python
match subject:
    case pattern if condition:
        # pattern が一致し、かつ condition が True の場合のみ実行されます

簡単な例を見てみましょう。

python
# 年齢に基づくアクセス制御
age = 16
 
match age:
    case age if age >= 18:
        print("Adult - full access granted")
    case age if age >= 13:
        print("Teen - limited access granted")
    case age if age >= 0:
        print("Child - parental supervision required")
    case _:
        print("Invalid age")

Output:

Teen - limited access granted

この例では、束縛変数 age が値を捕捉し、ガード if age >= 13 が追加条件を付けています。この case は値が 13 以上の場合にのみ一致します。age16 なので、2番目の case が一致して実行されます。

13.4.2) ガードの評価順序

評価順序を理解することは重要です。次は、ガードがパターンマッチングとどのように相互作用するかを示す詳細な可視化です。

No

Yes

Yes

No

Yes

No

case パターンをチェック

パターンは一致したか?

次の case を試す

ガード条件を評価

ガードは True か?

case ブロックを実行

match 文を終了

まだ case があるか?

一致なし - match の後へ継続

Python はまずパターンが一致するかをチェックします。パターンが一致した場合にのみ、Python はガード条件を評価します。ガードが偽(false)なら、パターンが一致していたとしても、Python は次の case に進みます。

これを示す例です。

python
# 温度警告システム
temperature = 25
 
match temperature:
    case temp if temp > 35:
        print(f"Extreme heat warning: {temp}°C")
    case temp if temp > 30:
        print(f"High temperature alert: {temp}°C")
    case temp if temp > 20:
        print(f"Comfortable temperature: {temp}°C")
    case temp if temp > 10:
        print(f"Cool temperature: {temp}°C")
    case temp:
        print(f"Cold temperature: {temp}°C")

Output:

Comfortable temperature: 25°C

各 case は束縛変数 temp を使って温度値を捕捉し、ガードで特定の範囲に入るかをチェックします。case は順番にチェックされるので、ガードが真である最初の一致 case が実行されます。

13.4.3) リテラルパターンとガード

ガードをリテラルパターンと組み合わせることで、より具体的なマッチングを作れます。

python
# 商品種別と数量に基づく割引計算
item = ("book", 5)
 
match item:
    case ("book", quantity) if quantity >= 10:
        discount = 0.20  # 10 冊以上の本は 20% 割引
        print(f"Bulk book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity) if quantity >= 5:
        discount = 0.10  # 5〜9 冊の本は 10% 割引
        print(f"Book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity):
        discount = 0.0  # 5 冊未満は割引なし
        print(f"Book order: {quantity} books, no discount")
    case (item_type, quantity):
        print(f"Order: {quantity} {item_type}(s)")

Output:

Book order: 5 books, 10.0% discount

パターン ("book", quantity) は、先頭要素が "book" のタプルに一致します。ガード if quantity >= 5 は、数量が少なくとも 5 であるという条件を追加します。

13.4.4) 複雑な条件のガード

ガードは、andornot を使った複雑な条件も含め、任意のブール式(boolean expression)を使えます。

python
# 出席率を考慮した学生評価
student = {"name": "Bob", "grade": 85, "attendance": 75}
 
match student:
    case {"grade": g, "attendance": a} if g >= 90 and a >= 90:
        status = "Excellent"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 80 and a >= 80:
        status = "Good"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 70 and a >= 70:
        status = "Satisfactory"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 60 or a >= 60:
        status = "Needs Improvement"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case _:
        print("Failing - immediate intervention required")

Output:

Grade: 85, Attendance: 75% - Status: Satisfactory

ガード if g >= 70 and a >= 70 は、成績と出席率の両方が少なくとも 70 であることを要求します。Bob は成績が 85、出席率が 75% なので、この case が一致します。

13.4.5) 実用例:ユーザー認証システム

ガードを使って現実的な認証システムを実装する完全な例を作ってみましょう。

python
# ロールベースのアクセスを含むユーザー認証
user = {"username": "alice", "role": "admin", "active": True, "login_attempts": 0}
 
match user:
    case {"active": False}:
        print("Account suspended - contact administrator")
    case {"login_attempts": attempts} if attempts >= 3:
        print("Account locked due to too many failed login attempts")
    case {"role": "admin", "active": True}:
        print("Admin access granted - full system privileges")
    case {"role": "moderator", "active": True}:
        print("Moderator access granted - content management privileges")
    case {"role": role, "active": True} if role in ["editor", "author"]:
        print(f"{role.capitalize()} access granted - content creation privileges")
    case {"role": "user", "active": True}:
        print("User access granted - basic privileges")
    case _:
        print("Access denied - invalid user profile")

Output:

Admin access granted - full system privileges

この例は、ガードが複雑なビジネスロジックを実装できることを示しています。システムは複数の条件(アカウント状態、ログイン試行回数、ロールベースの権限)をチェックします。各 case が特定のシナリオを扱うため、認証ロジックが明確で保守しやすくなります。

13.5) 単純なシーケンスとマッピングの形でマッチする

13.5.1) 可変長シーケンスにマッチする

長さが変化するシーケンスにマッチさせたい場合があります。Python のパターンマッチング(pattern matching)は * 演算子でこれをサポートしており、0個以上の要素を捕捉できます。

python
# 可変長引数を持つコマンドパーサ
command = ["copy", "file1.txt", "file2.txt", "file3.txt", "backup/"]
 
match command:
    case ["help"]:
        print("Available commands: copy, move, delete")
    case ["copy", source, destination]:
        print(f"Copying {source} to {destination}")
    case ["copy", *sources, destination]:
        print(f"Copying {len(sources)} files to {destination}")
        print(f"Source files: {sources}")
    case ["delete", *files]:
        print(f"Deleting {len(files)} file(s): {files}")
    case _:
        print("Unknown command")

Output:

Copying 3 files to backup/
Source files: ['file1.txt', 'file2.txt', 'file3.txt']

パターン ["copy", *sources, destination] は、"copy" で始まり、末尾が宛先で、その間に任意個のソースファイルを持つリストに一致します。*sources は中間要素をすべてリストとして捕捉します。

重要: 1つのシーケンスパターン内で * パターンを使えるのは1回だけで、捕捉される要素はリストになります。

python
# ログエントリパーサ
log_entry = ["2025-12-17", "10:30:45", "ERROR", "Database", "connection", "timeout"]
 
match log_entry:
    case [date, time, "ERROR", *error_details]:
        print(f"Error on {date} at {time}")
        print(f"Error details: {' '.join(error_details)}")
    case [date, time, "WARNING", *warning_details]:
        print(f"Warning on {date} at {time}")
        print(f"Warning details: {' '.join(warning_details)}")
    case [date, time, level, *message]:
        print(f"{level} on {date} at {time}: {' '.join(message)}")

Output:

Error on 2025-12-17 at 10:30:45
Error details: Database connection timeout

13.5.2) シーケンスパターンとガードを組み合わせる

シーケンスパターンにガードを使って追加条件を付けることができます。

python
# 成績リスト解析
grades = [85, 92, 78, 95, 88]
 
match grades:
    case []:
        print("No grades recorded")
    case [grade] if grade >= 90:
        print(f"Single excellent grade: {grade}")
    case [grade] if grade < 60:
        print(f"Single failing grade: {grade}")
    case [*all_grades] if len(all_grades) >= 5 and sum(all_grades) / len(all_grades) >= 90:
        average = sum(all_grades) / len(all_grades)
        print(f"Excellent performance! Average: {average:.1f}")
    case [*all_grades] if len(all_grades) >= 5:
        average = sum(all_grades) / len(all_grades)
        print(f"Performance review: {len(all_grades)} grades, Average: {average:.1f}")
    case [*all_grades]:
        print(f"Insufficient data: only {len(all_grades)} grade(s)")

Output:

Performance review: 5 grades, Average: 87.6

パターン [*all_grades] はリスト内の全要素を捕捉し、ガードは長さのチェックと平均の計算を行って、適切なメッセージを決定します。


matchcase によるパターンマッチング(pattern matching)は、Python で複雑な意思決定を扱うための強力で表現力の高い方法を提供します。単純な値の一致から、ガード付きの洗練された構造パターンまで、この機能により、複数ケースを扱ったり複雑な構造からデータを抽出したりするコードを、よりクリーンで保守しやすく書けるようになります。

Python の学習を続ける中で、パターンマッチング(pattern matching)は第8章で学んだ条件ロジックを補完し、複数の明確に分かれたケースを扱うとき、特にパターンベースの抽出が必要な構造化データを扱うときに、エレガントな代替手段になることが分かるでしょう。重要なのは、状況に応じて適切な道具を選ぶことです。単純な条件やブール論理には if-elif-else を使い、1つの値を複数の可能性と照合する場合や、パターンに基づいて抽出が必要な構造化データを扱う場合には match-case を使いましょう。

© 2025. Primesoft Co., Ltd.
support@primesoft.ai