カテゴリー

Python基礎

【Python3.10&パターンマッチ】match文を試してみる

2021年10月11日

こんな人にオススメ

2021年10月4日にpython3.10がリリースされ、新機能としてパターンマッチが追加されたけど、どんなもの?

ということで、今回はpython3.10で新たに追加された新機能「パターンマッチ」について解説する。「match」と「case」を使用して処理を分岐させる構文だ。

似ているものとしてはC言語のswitch文が挙げられる。今までpythonにはこういった条件分岐はなかった。

なお、今回のパターンマッチを使用しなくても、ifを使用することで代用することが可能。だけど、複雑な構造を簡単にかけるっぽい。

python環境は以下。

  • Python 3.10.0

運営者のメガネです。YouTubeTwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

基本的な書き方

match 調べたいオブジェクト:
    case パターン1:
        処理1
    case パターン2:
        処理2
    case _:
        パターン1, 2以外の場合の処理

基本的な書き方はシンプルで、本当にC言語のswitch文と同じイメージ。ただし、switchとは書かずにmatchと書く。

caseの後に判定したいパターンを入れていき、最後のcase _でパターンに当てはまらないもの全ての処理を行う。

最後のcase _はなくても大丈夫。そのときはパターンから外れると何も起きない。後ほど実行する。

数値でマッチ

# パターンマッチ
def match_simple(val):
    match val:
        case 0:  # valが0の時
            print('0でした')
        case 1:  # valが1の時
            print('1でした')
        case _:  # valが0, 1以外の時
            print('0, 1以外')

まずは数字でパターンマッチする。判定するのは以下の項目。

  • 0
  • 1
  • 上記以外

なお、小数1.を入れたとしても1判定される。

val = 0  # 調べたい変数
match_simple(val)
# 0でした
match_simple(val=10)  # この場合はマッチしないので「case _」に該当
# 0, 1以外

match_simple(val=1.)  # 小数も適用される
# 1でした

orは「|」で指定

# もしくはの時は「|」を使用
def match_or(val):
    match val:
        case 0 | 1:  # valが0か1の時
            print('0もしくは1でした')
        case 1:  # valが1の時(上のcaseで1は取られるのでここは適用されない)
            print('1でした')
        case _:  # valが上記以外の時
            print('0, 1以外')

パターンに「または」を入れたいときは「|」を使用する。上では0もしくは1で判定をしている。

なお、パターン1ですでに1が判定されているので、パターン2の1は判定されない。

match_or(val=0)
# 0もしくは1でした
match_or(val=1)
# 0もしくは1でした
match_or(val=2)
# 0, 1以外

orにするとエラー

# 「|」の代わりに「or」を使用するとエラー
def match_or2(val):
    match val:
        case 0 or 1:  # valが0か1の時
            print('0もしくは1でした')
        case 1:  # valが1の時(上のcaseで1は取られるのでここは適用されない)
            print('1でした')
        case _:  # valが上記以外の時
            print('0, 1以外')

#     case 0 or 1:  # valが0か1の時
#            ^^
# SyntaxError: expected ':'

逆に「|」の代わりに「or」を使用するとこれは文法エラーとなる。

andはそもそもエラー

# andは使えない
def match_and(val):
    match val:
        case 0 & 0.:  # valが0の時
            print('0でした')

#     case 0 & 0.:  # valが0の時
#            ^
# SyntaxError: expected ':'

andの場合はどうかというとこちらも文法エラー。andを使用する例が思いつかなかったので意味わからん判定になっているけどエラー。

後述するがパターンには式は入れられないので、len(val) == 3 and val[0] == 'a'のように文字列の長さと先頭文字の判定を入れることはできない。

上の例では「&」で試したけど、「and」にしてもエラーだった。

case _を省略するとそれ以外は何もしない

# 「case _」を省略するとそれ以外の時には何もしない
def match_no_others(val):
    match val:
        case 0 | 1:  # valが0か1の時
            print('0もしくは1でした')
        case 1:  # valが1の時(上のcaseで1は取られるのでここは適用されない)
            print('1でした')

それ以外のパターンであるcase _を省略することも可能。この場合はそれ以外のパターンでは何もしないことになる。

今回の例だと、01以外の入力では何も起きないことになる。

match_no_others(val=0)
# 0もしくは1でした

match_no_others(val=2)  # 「case _」を省くとそれ以外の判定は何もしないことになる

式は入れられないけど複素数なら大丈夫

# 式は入れられない
val = 1
match val:
    case 0 + 1:
        print('0 + 1を判定')
#     case 0 + 1:
#              ^
# SyntaxError: imaginary number required in complex literal

上で少し書いたけど、パターンの部分に式を入れる事はできない。この例だと0 + 1という式を入れたけどエラー。

しかし、式ではなく複素数とするとエラーとはならない。以下の例では0 + 1jという複素数を使用した。まあ実数と虚数だから分けざるを得ない。

# 複素数なら大丈夫
val = 0 + 1j
match val:
    case 0 + 1j:
        print('0 + 1jを判定')
# 0 + 1jを判定

ifを使うと条件追加(ガード)

# ifを使って条件を追加(ガード)
def match_req(val):
    match val:
        case val if val <= 1:
            print('1以下の値')
        case val if 1 < val <= 2:
            print('1 < val ≤ 2の値')
        case _:
            print('それ以外')

caseのパターンに条件を追加することも可能。これをガードと呼ぶようだ。かっこいい。

これは、caseのパターンに当てはまったものに追加で条件を追加するイメージ。今回だとval1以下かどうかと1を超過して2以下とした。

match_req(val=0)
# 1以下の値
match_req(val=1)
# 1以下の値
match_req(val=1.5)
# 1 < val ≤ 2の値
match_req(val=2)
# 1 < val ≤ 2の値
match_req(val=3)
# それ以外

listのマッチは個数判定

# listでマッチさせるときは要素数で判定
def match_lst(lst):
    match lst:
        case[1]:
            print('[1]でした')
        case[x]:
            print(f"[1]ではなく[{x}]でした")
        case[0, 1]:
            print('[0, 1]でした')
        case[x, y]:
            print(f"[0, 1]ではなく[{x}, {y}]でした")
        case _:
            print('それ以外')

listをパターンに持ってくることも可能。考え方は今までと同じでいける。なお、tupleも同じように可能。

初めのcase[1]を判定し、それ以外の要素数が1つのリストはパターン2の[x]で判定している。その後も同じ感じ。

match_lst(lst=[0])
# [1]ではなく[0]でした
match_lst(lst=[1])
# [1]でした
match_lst(lst=[0, 1])
# [0, 1]でした
match_lst(lst=[10, 11])
# [0, 1]ではなく[10, 11]でした
match_lst(lst=[10, 11, 20])
# それ以外

dictのマッチはkey判定

# 辞書型ではkeyで判定
def match_dct(dct):
    match dct:
        case {'key1': 0, 'key2': x}:
            print('0番目のケース')
        case dict(key1=1, key2=x):  # dict()で書くとスルーされる
            print('1番目のケース')
        case {'key1': 1, 'key2': x}:
            print('2番目のケース')
        case _:
            print('それ以外')

dictもパターンに持ってこれる。dictの場合はちょっと挙動が変になるので、マッチさせる際には注意が必要。

{key:: value}で作成したdictだとちゃんと判定されるけど、dcit(key=value)で作成すると判定されない。

色んなサイトの例を見ても{}を使用するばかりだったので、下手にdictで作成しないほうがいいだろう。

match_dct(dct={'key1': 0, 'key2': 10})
# 0番目のケース
match_dct(dct={'key1': 1, 'key2': 10})
# 2番目のケース
match_dct(dct=dict(key1=1, key2=10))
# 2番目のケース
match_dct(dct={'key1': 2, 'key2': 10})
# それ以外

型で判定

# 型で判定
def match_type(x):
    match x:
        case int():
            print('整数')
        case str():
            print('文字列')
        case list():
            print('リスト')
        case tuple():
            print('タプル')
        case dict():
            print('辞書')

最後は型の判定。caseにはクラス名で入れることもできる。ここでは簡単な型を判定させてみた。

match_type(x=10)
# 整数
match_type(x='10')
# 文字列
match_type(x=[10])
# リスト
match_type(x=(10,))
# タプル
match_type(x={'10': 10})
# 辞書

下手に使うことはないか

今回はpython 3.10の新機能であるパターンマッチを試してみた。これまではifを使ってちまちま書いていたのがmatchを使用することで簡単に書ける。

しかし、実際にはまだこれを活用する場面が思い浮かばないし、下手に使うと他の人が使えないってことになりかねない。

ということで、パターンマッチはしばらくは使わないほうがいいか、自分用に使うのがいいだろう。

関連記事

【辞書の結合】dictのマージ

こんな人にオススメlistは+記号で配 ...

続きを見る

【astropy&単位】astropyの単位出力書式

こんな人にオススメastropyで数値に単& ...

続きを見る

【python3.7以降&dictのkeys】ネスト(入れ子)されたdictのkeys一覧をカッコで出力

こんな人にオススメ dictとやらに手 ...

続きを見る

【python3&zip関数】python3系にてzipは一回使ったら消える

こんな人にオススメ python3系でzip関数| ...

続きを見る

スマホ

2023/1/21

【Galaxy S22 Ultraレビュー】これが最高峰

こんな人におすすめ 2022年最強のスマホGalaxy S22 Ultraって実際使った感じどうなの ...

完全ワイヤレスイヤホン(TWS)

2023/1/15

【SENNHEISER MOMENTUM True Wireless 3レビュー】高レベルでバランス型の高音質イヤホン

こんな人におすすめ SENNHEISER MOMENTUM True Wireless 3って実際のところどうなの? 評判は良い ...

完全ワイヤレスイヤホン(TWS)

2023/1/14

【SONY WF-1000XM4 vs SENNHEISER MTW3】MTW3を選んだ決定的な3つの理由

こんな人におすすめ 執筆者は「SONY WF-1000XM4」「SENNHEISER MOMENTUM True Wireless 3」両方を持っている ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/1/5

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

マウス

2023/1/14

【Logicool MX ERGOカスタム】Logi Optionsのジェスチャーボタン設定内容

こんな人におすすめ Logicool MX ERGOをもっと上手に効率的に使いこなしたい。 ボ| ...

生活に役立つ

2023/1/8

【メガネ厳選】クソ便利に使っているサービスやアイテム達

このページでは執筆者「メガネ」が実際に使って便利だと感じているサ ...

完全ワイヤレスイヤホン(TWS)

2023/3/24

【SONY WF-1000XM4レビュー】神とゴミのハーフ&ハーフ

こんな人におすすめ SONYのフラグシップモデル「SONY WF-1000XM4」ってどれくらい性 ...

スイッチボット

2023/1/14

【SwitchBot Hub Mini】アプリにないエアコンなどの家電をその他で登録する方法

こんな人におすすめSwitchBot Hub Miniに我が家のエアコンを登録したいけど、SwitchBotア ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→新卒を1.5年で辞める→転職→今に至る。
常時金欠のガジェット好きでM1 MacBook Pro x Galaxy S22 Ultraの狂人。

自己紹介と半生→変わって楽しいの繰り返し レビュー依頼などお問い合わせ Twitter@m_ten_pa

-Python基礎
-