「分岐処理を綺麗にコーディングしたい」
「Pythonでswitch文を使いたい」
「辞書型データの構造的パターンマッチとは?」
このような場合には、辞書型データの構造的パターンマッチがオススメです。
この記事では、辞書型データの構造的パターンマッチについて解説しています。
本記事の内容
- 辞書型データの構造的パターンマッチ
- 辞書型データの構造的パターンマッチの使い方(その1)
- 辞書型データの構造的パターンマッチの使い方(その2)
それでは、上記に沿って解説していきます。
辞書型データの構造的パターンマッチ
構造的パターンマッチは、Python 3.10の新機能です。
一言で言うと、switch文と同じ機能があります。
switch文としての利用については、次の記事でまとめています。
上記記事では、定数値によるパターンマッチについて解説しています。
つまり、世間で言うswitch文の機能です。
辞書型データの構造的パターンマッチは、switch文にプラスアルファされたモノと言えます。
辞書型データの構造的パターンマッチの機能は、以下となります。
- 処理の分岐
- 変数への値の代入
switch文は、処理の分岐を行います。
それに加えて、変数への値の代入も同時に実施されます。
おそらく、機能だけ見ても構造的パターンマッチの良さは伝わりません。
実際にコードで確認すれば、構造的パターンマッチの良さがわかるはずです。
以下では、実際にコードを元に構造的パターンマッチを確認していきます。
辞書型データの構造的パターンマッチの使い方(その1)
その1で紹介するのは、以下のコードです。
# OK or NG PATTERN = "OK" def get_result(ptn): result = {} if ptn == "OK": result["state"] = "success" result["name"] = "テスト太郎" result["mailaddress"] = "testtaro@example.com" else: result["state"] = "error" result["message"] = "データがありません" return result if __name__ == '__main__': dic_result = get_result(PATTERN) match dic_result: case {"state": "success", "name": username, "mailaddress": mail}: print(username + "のメールアドレスは、" + mail) case {"state": "error", "message": error}: print(error)
get_result関数は、引数によって戻す値が異なります。
そして、どのパターンでも辞書型のデータを戻します。
肝心の構造的パターンマッチは、match文の部分です。
処理の分岐と変数への値の代入を同時に行っています。
そして、上記コードをそれぞれのパターンで実行した結果は以下。
パターンは、PATTERNで制御可能です。
OK
テスト太郎のメールアドレスは、testtaro@example.com
NG
データがありません
上記のコードを見て、どう感じましたか?
「if文だけでコーディングできるじゃん」
このように思いますよね。
実際、次のようにmatch文の部分をコーディングできます。
if dic_result["state"] == "success": username = dic_result["name"] mail = dic_result["mailaddress"] print(username + "のメールアドレスは、" + mail) elif dic_result["state"] == "error": error = dic_result["message"] print(error)
正直、その1のケースならmatch文でもif文でも、それほど違いはありません。
若干、match文の方がわかりやすいという程度です。
この程度なら、使い慣れたif文のままという人は多いでしょう。
辞書型データの構造的パターンマッチの本領発揮は、次のような場合です。
辞書型データの構造的パターンマッチの使い方(その2)
その2で紹介するのは、以下のコードです。
# OK or NG PATTERN = "OK" def get_result(ptn): result = {} if ptn == "OK": result["state"] = "success" result["name"] = "テスト太郎" result["mailaddress"] = "testtaro@example.com" else: result["error"] = "データがありません" return result if __name__ == '__main__': dic_result = get_result(PATTERN) match dic_result: case {"state": "success", "name": username, "mailaddress": mail}: print(username + "のメールアドレスは、" + mail) case {"error": error}: print(error)
get_result関数は、引数によって戻す値が異なります。
そして、どのパターンでも辞書型のデータを戻します。
ここまではその1と同じです。
ただし、辞書型データのキーに注目してください。
NGの場合には、「state」キーがありません。
こんな嫌なデータの持ち方は、意味わかりませんよね。
でも、このようなデータを戻すAPI・関数は普通に存在しています。
しかし、それに関わらず構造的パターンマッチは機能します。
これをif文で書こうとすると、ちょっと工夫が必要になります。
単純に次のようにコーディングすると、NGの場合にエラーが発生します。
if dic_result["state"] == "success": username = dic_result["name"] mail = dic_result["mailaddress"] print(username + "のメールアドレスは、" + mail) elif dic_result["error"]: error = dic_result["error"] print(error)
エラーは、以下。
KeyError: 'state'
このエラーを避けるには、get関数を利用します。
if dic_result.get("state") == "success": username = dic_result["name"] mail = dic_result["mailaddress"] print(username + "のメールアドレスは、" + mail) elif dic_result.get("error"): error = dic_result["error"] print(error)
ソースが、ちょっとゴチャゴチャしてきましたね。
分岐がもっと増えてきたら、ソースがさらにゴチャゴチャしてしまいます。
そう感じた場合、構造的パターンマッチを思い出してみてください。