Python での構造パターン マッチング
Python 3.10 より前は、他のプログラミング言語で switch-case
と呼ばれる構造パターン マッチングを使用する組み込みの方法がありませんでした。 Python 3.10 リリースの時点で、match ... case
ステートメントを使用して switch ... case
ステートメントをエミュレートすることはできません。
このチュートリアルでは、構造パターン マッチングと Python におけるその重要性を紹介します。 また、match ... case
ステートメントの使用方法を示すために、さまざまなパターンを使用しています。
構造パターン マッチングの概要とその重要性
2021 年の初めの時点で、3.9 以下のリリースされた Python バージョンでは match
キーワードを使用できませんでした。 当時、私たちは、辞書やネストされた if/elif/else
ステートメントを使用して switch ... case
をシミュレートすることに慣れていました。
しかし、Python 3.10 では、構造パターン マッチング (match ... case
ステートメント) と呼ばれる新しい機能が導入されました。 これは、Java、C++、および他の多くのプログラミング言語にあるような switch ... case
ステートメントと同等です。
この新機能により、シンプルで読みやすく、最小限のエラー フロー制御ステートメントを記述できるようになりました。
Python で構造パターン マッチングを使用する
構造パターン マッチングは switch ... case
ステートメントとして使用され、これよりも強力です。 どうやって? 以下のいくつかの例を調べて、さまざまな状況での使用法を学びましょう。
match ... case
ステートメントの基本的な使い方
コード例:
# >= Python 3.10
colour = "blue"
match colour:
case "green":
print("The specified colour is green")
case "white":
print("Wow, you've picked white")
case "green":
print("Great, you are going with green colour")
case "blue":
print("Blue like sky...")
出力:
Blue like sky...
ここでは、最初に blue
を含む変数 colour
があります。 次に、match
キーワードを使用します。これは、colour
変数の値をさまざまな指定されたケースに一致させます。各ケースは、case
キーワードで始まり、その後に比較またはチェックするパターンが続きます。
パターンは次のいずれかです。
- リテラルパターン
- 攻略パターン
- ワイルドカード パターン
- 定値パターン
- シーケンスパターン
- マッピング パターン
- クラスパターン
- OR パターン
- セイウチ柄
match ... case
ステートメントは、一致した最初の case
でのみコードを実行します。
case
が一致しない場合はどうなりますか? ユーザーはそれについてどのように知ることができますか? そのために、次のようにデフォルトの case
を設定できます。
コード例:
# >= Python 3.10
colour = "yellow"
match colour:
case "green":
print("The specified colour is green")
case "white":
print("Wow, you've picked white")
case "green":
print("Great, you are going with green colour")
case "blue":
print("Blue like sky...")
case other:
print("No match found!")
出力:
No match found!
match ... case
を使用してデータ構造を検出および分解する
コード例:
# >= Python 3.10
student = {"name": {"first": "Mehvish", "last": "Ashiq"}, "section": "B"}
match student:
case {"name": {"first": firstname}}:
print(firstname)
出力:
Mehvish
上記の例では、次の 2 行のコードで構造パターン マッチングが実行されています。
# >= Python 3.10
match student:
case {"name": {"first": firstname}}:
match ... case
ステートメントを使用して、student
データ構造から抽出することにより、学生の名前を見つけます。 ここで、student
は生徒の情報を含む辞書です。
case
行は、student
に一致するパターンを指定します。 上記の例を考えると、値が新しい辞書である the "name"
キーを持つ辞書を探します。
このネストされた辞書には、値が firstname
変数にバインドされた "first"
キーが含まれています。 最後に、firstname
変数を使用して値を出力します。
もっと深く観察すれば、ここでマッピングパターンを学びました。 どうやって? マッピング パターンは {"student": s, "emails": [*es]}
のようになり、少なくとも指定されたキーのセットとマッピングに一致します。
すべてのサブパターンが対応する値と一致する場合、キーに対応する値との照合中にサブパターン バインドがバインドされます。 追加のアイテムをキャプチャできるようにしたい場合は、パターンの最後に **rest
を追加できます。
キャプチャ パターンとシーケンス パターンで match ... case
を使用する
コード例:
# >= Python 3.10
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [first, *rest]:
return first + sum_list_of_numbers(rest)
sum_list_of_numbers([1, 2, 3, 4])
出力:
10
ここでは、再帰関数を使用してキャプチャ パターンを使用し、指定されたパターンに一致するものをキャプチャして名前にバインドします。
このコード例では、最初の case
は、空のリストと一致する場合、合計として 0
を返します。 2 番目の case
は、複数のアイテム/要素のいずれかとリストを照合するために、2つのキャプチャ パターンを含むシーケンス パターンを使用します。
ここでは、リストの最初のアイテムがキャプチャされ、first
という名前にバインドされます。2 番目のキャプチャ パターン *rest
は、アンパック構文を使用して任意の数のアイテム/要素に一致します。
rest
は、最初のものを除いて、数値のすべての項目/要素を持つリストにバインドされることに注意してください。 出力を取得するには、上記のように数値のリストを渡して sum_list_of_numbers()
関数を呼び出します。
ワイルドカード パターンで match ... case
を使用する
コード例:
# >= Python 3.10
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [first, *rest]:
return first + sum_list_of_numbers(rest)
case _:
incorrect_type = numbers.__class__.__name__
raise ValueError(
f"Incorrect Values. We Can only Add lists of numbers,not {incorrect_type!r}"
)
sum_list_of_numbers({"1": "2", "3": "4"})
出力:
ValueError: Incorrect Values. We Can only Add lists of numbers, not 'dict'
match ... case
ステートメントの基本的な使い方を学びながら、ワイルドカード パターンを使用する概念を学びましたが、そこではワイルドカード パターンの用語を紹介しませんでした。 最初の 2つのケースが一致せず、最後の ケース
としてキャッチオール パターンが必要なシナリオを想像してみてください。
たとえば、リストの代わりに他のタイプのデータ構造を取得した場合にエラーを発生させたいと考えています。 ここでは、ワイルドカード パターンとして _
を使用できます。これは、名前にバインドせずに何にでも一致します。 この最後の case
にエラー処理を追加して、ユーザーに通知します。
あなたは何を言っていますか? 私たちのパターンはうまくいくでしょうか? 次のように文字列値のリストを渡して sum_list_of_numbers()
関数を呼び出してテストしてみましょう。
sum_list_of_numbers(["1", "2", "3", "4"])
次のエラーが発生します。
TypeError: can only concatenate str (not "int") to str
したがって、パターンはまだ十分に確実ではないと言えます。 なぜ? リスト型のデータ構造を sum_list_of_numbers()
関数に渡しますが、期待した int 型ではなく文字列型の値を持っているためです。
解決方法については、次のセクションを参照してください。
クラス パターンで match ... case
を使用する
コード例:
# >= Python 3.10
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [int(first), *rest]:
return first + sum_list_of_numbers(rest)
case _:
raise ValueError(f"Incorrect values! We can only add lists of numbers")
sum_list_of_numbers(["1", "2", "3", "4"])
出力:
ValueError: Incorrect values! We can only add lists of numbers
基本ケース (最初の case
) は 0
を返します。 したがって、合計は、数値で加算できる型に対してのみ機能します。 Python は、テキスト文字列と数字を追加する方法を知らないことに注意してください。
そのため、クラス パターンを使用して、パターンを整数のみに一致するように制限できます。 クラス パターンはマッピング パターンに似ていますが、キーではなく属性に一致します。
OR パターンで match ... case
を使用する
コード例:
# >= Python 3.10
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [int(first) | float(first), *rest]:
return first + sum_list_of_numbers(rest)
case _:
raise ValueError(f"Incorrect values! We can only add lists of numbers")
sum_list_of_numbers()
関数を、int 型または float 型の値に関係なく、値のリストに対して機能させたいとします。 パイプ記号 (|
) で表される OR パターンを使用します。
上記のコードは、指定されたリストに int または float 型の値以外の値が含まれている場合、ValueError
を発生させる必要があります。 以下の 3つのシナリオすべてを考慮してテストしてみましょう。
テスト 1: int 型の値を持つリストを渡します。
sum_list_of_numbers([1, 2, 3, 4]) # output is 10
テスト 2: float 型の値を持つリストを渡す:
sum_list_of_numbers([1.0, 2.0, 3.0, 4.0]) # output is 10.0
テスト 3: int 型と float 型を除く他の型を持つリストを渡します。
sum_list_of_numbers(["1", "2", "3", "4"])
# output is ValueError: Incorrect values! We can only add lists of numbers
ご覧のとおり、sum_list_of_numbers()
関数は、OR パターンを使用しているため、int 型と float 型の両方の値に対して機能します。
match ... case
をリテラル パターンで使用する
コード例:
# >= Python 3.10
def say_hello(name):
match name:
case "Mehvish":
print(f"Hi, {name}!")
case _:
print("Howdy, stranger!")
say_hello("Mehvish")
出力:
Hi, Mehvish!
この例では、match ... case
ステートメントの基本的な使用法を学習するときに既に行ったように、明示的な数値や文字列など、リテラル オブジェクトに一致するリテラル パターンを使用します。
これはパターンの最も基本的なタイプであり、Java、C++、およびその他のプログラミング言語に似た switch ... case
ステートメントをシミュレートできます。 このページにアクセスして、すべてのパターンについて学習できます。