フリーキーズ | 独学プログラミング

Pythonでswitch-caseのように使えるmatch分の使い方

最終更新日
【画像】Pythonでswitch-caseのように使えるmatch分の使い方

Pythonでswitch-case文を使いたい、という要望は昔から上がっていました。(特にPython2時代を知るエンジニアたちから)
多くの議論がされ、Python 3.10でついに実装されたのがmatch構文です。

それまで、if/elifで対応され続けてきた、複雑な条件分岐をすっきり書くようになったという大きな進化です。
このコンテンツでは、可読性の高いPythonの機能であるmatch文の使い方を紹介します。

Pythonのmatch文とは

Pythonにおけるmatchとは、PEP(Pythonにおける機能改善の設計書)においてPEP634で定義されたPython 3.10の新機能でした。
多くのプログラミング言語で実装されているパターンマッチング構文ですが、Pythonはif-elif-elseを利用した限定的な書き方しかなかったことから生まれました。

match文によって、よりクリーンで可読性の高いPythonコーディングができるようになったという革新的な機能です。

matchの基本

まずはmatchの書き方の構造を見てみましょう。

match 変数:
    case パターンA:
        return 返り値A
    case パターンB:
        return 返り値B
    case パターンC:
        return 返り値C
    case _:
        return デフォルト値

上記のように、すっきりした書き方ができます。
とある変数に対して、どういった値で渡されたかによって中の構文を切り替えられます。(上記の例だとパターンごとにreturnしています)

matchを使った簡単な例

簡単な実装例を見てみましょう。

def animal_says(animal: str) -> str:
    match animal:
        case "dog":
            return "わんわん!"
        case "cat":
            return "にゃーん!"
        case "duck":
            return "ぐわっ!"
        case _:
            return "おっす!"

if __name__ == "__main__":
    print(animal_says("dog"))
    print(animal_says("cat"))
    print(animal_says("duck"))
    print(animal_says("cow"))

このスクリプトを実行すると、以下のような出力結果が得られます。

$ python match.py
わんわん!
にゃーん!
ぐわっ!
おっす!

ご覧のように、animalという変数に対してマッチした結果が返ってきました。

matchとif-elseの比較

先ほどの例をif-elseで表現してみます。

def animal_says(animal: str) -> str:
    if animal == 'dog':
        return "わんわん!"
    elif animal == 'cat':
        return "にゃーん!"
    elif animal == 'duck':
        return "ぐわっ!"
    else:
        return "おっす!"

if __name__ == "__main__":
    print(animal_says("dog"))
    print(animal_says("cat"))
    print(animal_says("duck"))
    print(animal_says("cow"))

上記のように、animal ==を何度も書かなくて済むので省コード化されます。この程度であれば、if-elseでもいい、と感じる人もいるでしょうが、これがより多くの条件を含めば含むほどmatchが効果を発揮します。

matchの実用的な使用例

簡単な実装例だけでちょっとしたマッチングパターンは綺麗に実装できますが、これをもう少し発展させてみましょう。

計算式でパターンマッチング

まず、アンチパターンになりますが、計算式を条件にしたい場合はif-elif-elseを使うほうが良いでしょう。
以下に例を示します。

from typing import List, Tuple


def num_lists(nums: List[int]) -> Tuple[List[int], List[int], List[int]]:
    even = []
    one_third = []
    others = []

    for i in nums:
        match i:
            case i if i % 2 == 0:
                even.append(i)
            case i if i % 3 == 0:
                one_third.append(i)
            case _:
                others.append(i)

    return even, one_third, others

if __name__ == '__main__':
    [print(l) for l in num_lists(range(10))]

クラスでパターンマッチング

続いて、クラスによるパターンマッチングの例です。

class Human:
    has_philosophy: bool

class Animal:
    has_instinct: bool

def indentify(x):
    match x:
        case Human():
            return "Human"
        case Animal():
            return "Animal"
        case _:
            return "Unknown"


if __name__ == "__main__":
    obj = Human()
    obj.has_philosophy = True
    print(f'I am a {indentify(obj)}')
    obj = Animal()
    obj.has_instinct = True
    print(f'I am an {indentify(obj)}')
    print(f'I am an {indentify(0)}')

上記の出力結果は以下です。

$ python match.py
I am a Human
I am an Animal
I am an Unknown

特定の変数がどのクラスオブジェクトかを判定できます。通常、if-elseでクラスを判定するためにはisinstance()を使いますが、その冗長さを回避できます。

matchでクリーンなコードを書こう

ここまで、Pythonのmatch文を見てきましたが、連なりすぎるif-else文はそもそものコード設計に難ありなことが多いです。
ですので、matchを使うこと自体がおかしいのでは、と見直すこともありますが、ゲームシステムのような多くのクラスを有するプロジェクトでは使う機会があるでしょう。

プロジェクトが肥大化すればするほど、クリーンなコードが求められることなるので、ぜひこの機会に使い方を覚えて有効活用していきましょう。

関連するコンテンツ