パターンマッチングメカニズムは、Python 3.10で構造的パターンマッチングという名称で登場し、match-case構文によって実装されています。このツールを使用すると、複雑なデータ構造をエレガントかつ簡潔に分析し、パターンにマッチさせることができます。
多くの関数型言語(例:Haskell、Scala)では、パターンマッチングはデータ構造に基づいてロジックを分岐させる便利な方法と見なされてきました。Pythonは長い間そのようなメカニズムを持たず、if-elif-elseチェーンまたはアンパックによって対処していました。
複雑なネストされたデータ構造(辞書、リスト、オブジェクト)は、しばしば型、値、構造に対する複数のチェックを必要とし、条件が多すぎる混乱したコードを生むことがあります。
match-caseは、条件を煩雑にせず、型と構造を考慮したさまざまなデータ処理シナリオを宣言的で読みやすい方法で記述する手段を提供します。
def process(event): match event: case {"type": "click", "x": x, "y": y}: return f"Click at ({x}, {y})" case [command, *args]: return f"Command: {command}, args: {args}" case _: return "Unknown event" print(process({"type": "click", "x": 2, "y": 5})) # Click at (2, 5) print(process(["RUN", 1, 2, 3])) # Command: RUN, args: [1, 2, 3]
match-caseは値にのみマッチしますか? いいえ。マッチングは、構造、型、コレクションのアンパック、さらにはオブジェクトのプロパティ(特別なメソッド__match_args__を指定する場合)に基づいて行うことができます。
match内のcaseの順序は影響しますか? はい。分析は上から下へ行われ、最初に一致するパターンがトリガーされます。以降のcaseはチェックされません。
カスタムクラスをマッチさせることはできますか? はい、クラスが__match_args__および/または__getitem__メソッドを実装している場合、パターンマッチングはフィールドの値を抽出できるようになります。
class Point: __match_args__ = ("x", "y") def __init__(self, x, y): self.x = x self.y = y def where(obj): match obj: case Point(x, y): return f"Point at {x}, {y}" case _: return "Unknown" print(where(Point(1, 2))) # Point at 1, 2
利点:
ネガティブケース: 開発者たちは簡単なブール条件でもmatch-caseを使用し、コードが複雑になってしまいました。利点:新機能を学びました。欠点:可読性を失い、エラーが増えました。
ポジティブケース: 多数のイベントとネストされた構造を持つアプリケーション(例:グラフパーサー)では、match-caseを使用することで、冗長なif-elifを回避し、パース処理を集中化できました。利点:可読性が向上し、バグが減りました。欠点:新しい構文を使いこなすためにチームのトレーニングが必要でした。