0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python の match 文が便利

Last updated at Posted at 2024-11-24

はじめに

Python 3.10 で導入された Match 文が便利だという話です。
この記事はPython 3.13 の環境で試しています。

match 文とは

Match文は、Python 3.10で導入された構文であり、他の言語でいう switch 文、パターンマッチングに相当します。

match value:
    case pattern1:
        # パターン1に一致した場合の処理
    case pattern2:
        # パターン2に一致した場合の処理
    case _:
        # それ以外の場合の処理

基本はこのようなものです。

switch 文との違い?

作業中
いろいろな言語に switch 文・式というのがある。それらとの違いを示して match 文の特徴を浮き彫りにする。

  • 要素
    • 素朴にリテラル・定数を対象に取る
      • C言語、 JavaScript
    • 列挙型など多様なデータ構造を対象に取る
      • Python、 Ruby
    • 網羅性検査
      • Rust、 swift

とりあえず違いを考えてみたが、各々機能を拡張しているので長期的には大きな違いはなくなっていく気もする。

サンプル1: 値を取り込む(API を叩く)

match 文のマッピングパターンでオブジェクトから値を取り込むことが出来ます。
例としてメトロポリタン美術館のAPIを叩いてみます。

import json
import time
import requests


def met_api(query: str = "sunflowers") -> None:
    """ キーワード検索
    Document URL:https://metmuseum.github.io/
    """
    domain = "https://collectionapi.metmuseum.org"

    request = requests.get(
        f"{domain}/public/collection/v1/search?q={query}&=hasImages=true", timeout=10
    )

    match request.status_code:
        case 200:
            match json.loads(request.text):
                case {"total": total, "objectIDs": ids} if total > 0:
                    for id in ids:
                        print(f"https://www.metmuseum.org/art/collection/search/{id}")
        case _:
            pass
                             

サンプル2: 型に基づく処理の分岐が容易になる(ポーランド記法の計算)

こんなふうに型に基づく処理の分岐ができます。

match value:
    # | で OR を表す
    case int() | float():
        return value
    case _:
        raise ValueError("無効な式です")

これによって分岐が見やすくなり、値を取り込む機能と合わせて、次のようなことが簡単にできます。

"""ポーランド記法の計算"""
type Pn = tuple[str, Pn|int|float, Pn|int|float]
def pn_calculator(expression: Pn|int|float) -> int | float:
    """再帰して式を計算する。これによって入れ子のタプルでも計算可能"""
    match expression:
        case int() | float():
            return expression
        case ("+", left, right):
            return pn_calculator(left) + pn_calculator(right)
        case ("-", left, right):
            return pn_calculator(left) - pn_calculator(right)
        case ("*", left, right):
            return pn_calculator(left) * pn_calculator(right)
        case ("/", left, right):
            return pn_calculator(left) / pn_calculator(right)
    raise ValueError("無効な式です")

print(pn_calculator(("+", 2, 3))) # 5
print(pn_calculator(("+", ("+",1,1), 3))) # 5
print(pn_calculator(("+", ("/",5.0,2), 3))) #5.5

サンプル3:パターンマッチによる網羅性検査(型チェッカーに実装の漏れを検出させる)

Rust などには、分岐する可能性があるのを見逃しているとエラーを出す機能があります。
Python にも型チェッカーを利用すると同じようなことが出来ます。

""" match case で網羅性検査 """

import enum
from typing import Never


def assert_never(arg: Never) -> Never:
    """網羅性チェック"""
    raise AssertionError("Expected code to be unreachable")


class SHIKOKU(enum.Enum):
    """四国の特産品を列挙したクラス"""

    KAGAWA = "うどん"
    TOKUSHIMA = "すだち"
    EHIME = "みかん"
    KOUCHI = "かつお"


def main(shikoku: SHIKOKU):
    """ Never 型による網羅性検査"""
    match shikoku:
        case shikoku.KAGAWA:
            return shikoku.KAGAWA.value
        case shikoku.TOKUSHIMA:
            return shikoku.TOKUSHIMA.value
        case shikoku.EHIME:
            return shikoku.EHIME.value
        # case が網羅されていないと下のような警告を出す
        # 型 "Literal[SHIKOKU.KOUCHI]" の引数を、関数 "assert_never" の型 "Never" のパラメーター "arg" に割り当てることはできません
        # 型 "Literal[SHIKOKU.KOUCHI]" は型 "Never" に割り当てできません
        #case shikoku.KOUCHI:
        #    return shikoku.KOUCHI.value
        case _:
            assert_never(shikoku)


if __name__ == "__main__":
    for cnt, i in enumerate(SHIKOKU):
        match cnt:
            case 0:
                print("ズルル〜")
            case _ if cnt > 0:
                print("パクパク")
        print(cnt, main(i))

終わりに

Match文がとても便利。
マッピングパターンが便利。
型に基づく分岐が簡潔にできて便利。

Link

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?