【Python】辞書型データの構造的パターンマッチ

【Python】辞書型データの構造的パターンマッチ プログラミング

「分岐処理を綺麗にコーディングしたい」
「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)

ソースが、ちょっとゴチャゴチャしてきましたね。
分岐がもっと増えてきたら、ソースがさらにゴチャゴチャしてしまいます。

そう感じた場合、構造的パターンマッチを思い出してみてください。

タイトルとURLをコピーしました