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"
: 全部でnumber
x="123A"
:search_in
、match_in
でnumber
x="A123"
:search_in
のみnumber
となります。
regex_spm
は上のStrRe
に比べてちょっと入力側で色々出来るようになっていて、
中で正規表現側を一旦re.compile
してますが、その第二引数のflagを渡すようにtupleで比較することも出来るようになってます。
ちょっとした環境でなら自分でクラスを作ってしまってもそれほど大したことないですが、
色々とライブラリを導入するような環境ならregex_spm
を入れて使っても良いかな、と思います。