Pythonのmatch-case文
Python 3.10からmatch-caseを使った構造的パターンマッチ(match-case文)が導入されました。
- PEP 634 – Structural Pattern Matching: Specification peps.python.org
- PEP 635 – Structural Pattern Matching: Motivation and Rationale peps.python.org
- PEP 636 – Structural Pattern Matching: Tutorial peps.python.org
1 2 3 4 5 6 7 8 9 10 | |
みたいな感じでmatchのあとに来たものをcaseの各行で評価して該当してたらその中のものを実行します。
最後の_はシェルスクリプトのcase文の*みたいなものですべての状態に合致するため、
最後に持ってくるとC++のdefaultみたいな感じで全部が該当しなかった場合に実行されるものになります。
複数の値でorでみたいときは|で
1 2 3 4 5 6 7 8 | |
みたいな感じで複数をチェックできます。
_の代わりに適当な変数を与えると、それも必ず合致するものになり、かつその変数にmatchの値が代入されます。
1 2 3 4 5 6 7 8 | |
また、各caseには後ろにif文を書くことができ、これと上の代入の方法を組み合わせると、
1 2 3 4 5 6 7 8 | |
みたいな感じでより複雑なチェックを行うことが出来ます。
(このくらいだとif-elseの方がむしろシンプルなのでそうすべきな感じですが。)
また、このように変数をcaseのあとに置くとそこにmatchの値が代入されるため、
別の変数との比較、といった場合にはifと組み合わせて書く必要があります。
1 2 3 4 5 6 7 8 9 10 | |
な感じ。
xには複数の値(式)を置くことも可能で、
1 2 3 4 5 6 7 8 9 | |
みたいな感じでcaseの方も複数に対応するものを書いて、
その数と型、値が全部合致したものが実行されるようになっています。
(matchのあと、caseのあといずれもlistやtupleになっていても同じように各箇所があってるかどうか、で判断されます。)
他にも色々出来ることはありますが、PEPのtutorialとかを参考に。
正規表現でのパターンマッチ
シェルスクリプトとかだとabc*でabcから始まる文字列にマッチしたいさせる正規表現が使えますが、
Pythonの構造的パターンマッチでは同じようには出来ません。
PEP 634の元となった PEP 622の Custom matching protocol という項目に
There were ideas for exotic matchers such as IsInstance(), InRange(), RegexMatchingGroup() and so on.
とあるのでそれっぽいものは考えられてたみたいですが導入は見送られたようです。
1 2 3 4 5 6 7 8 9 | |
みたいな感じで正規表現を書いてそれにマッチしてくれると嬉しいところですが
これだとcaseのあとのものはそれぞれの文字がそのまま評価されるので123とかはマッチしません。
直接的にそのまま正規表現をcaseに渡すことは出来ませんが、
評価するものは文字列以外のものでも良いし、
match渡されたものを直接if文で評価することも可能なので色々やる方法はありそうです。
ifでチェックする
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
みたいな感じにすればnumberになります。
ただこれだとif-elseで書いたほうがむしろシンプル。
__eq__で正規表現matchを行う
caseではmatchで与えられたものとそこに書かれたものが==(__eq__)
で評価されているだけです。
なので文字列を正規表現で評価したいなら文字クラスを継承して
__eq__の部分で正規表現との比較を出来るようにしてあげればよい、という方法が
現状では一番スマートに見える解になっているようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Ref: Recipes and Tricks for Effective Structural Pattern Matching in Python Martin Heinz
こんな感じのStrReというstrを継承して__eq__だけ正規表現でマッチするように変更したクラスを作って
そのオブジェクトを作ってmatchにわたすことでcase側には正規表現な文字列を渡すだけでチェックできるようになってます。
これだとmatch-case文を使って書いたことでスッキリした感じが強いです。
ライブラリを使う
似たようなものですがライブラリとして公開されているものがあります。
これを
$ pip install regex-spm
でインストールして、
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
みたいな感じで使います。
使えるのはsearch_in、match_in、fullmatch_in
でそれぞれre.search、re.match、re.fullmatchに対応しています。
search_in: 部分マッチmatch_in: 先頭からの部分マッチfullmatch_in: 完全マッチ
なので、
x="123": 全部でnumberx="123A":search_in、match_inでnumberx="A123":search_inのみnumber
となります。
regex_spmは上のStrReに比べてちょっと入力側で色々出来るようになっていて、
中で正規表現側を一旦re.compileしてますが、その第二引数のflagを渡すようにtupleで比較することも出来るようになってます。
ちょっとした環境でなら自分でクラスを作ってしまってもそれほど大したことないですが、
色々とライブラリを導入するような環境ならregex_spmを入れて使っても良いかな、と思います。
