Pythonにはswitch文はありません。
そして3.10未満ではswitchの代替構文もなかったです。
ところがver3.10以上ならmatch~case文が使用可能
どんなのかなと気になったので使ってみました。
またPython3.1未満でのmatch~case文の代替も考えてみます。
このページの目次
ほとんどの言語ではswitch文は忌み嫌われている
switch文には次の問題点があります。
- バグの温床になる break の付け忘れ
- 特定言語では厳密な型チェックなし
このswitch文をサポートしてる言語すべて(JavaScript、PHP、C言語、Java、何でも…)に言えることです。この仕様は本当に害悪極まりない
だからこんな記事まで過去に書いたほどです。
▼ switch文の代替をJavaScriptで考えてみた
結局のところ、switch文は構文的には便利です。
でも break 付け忘れのリスクがデカい
それがswitch文が忌み嫌われてる理由ですね。同意
Python3.10以降は match~case文 の導入で便利になる
最新のPythonでは match~case文 が使えます。
といってもPython3.10以降ですけどね…
大抵はver3.6とかなので普及はまだ先の話かも
でも本当に match~case文 は便利なんです。
▼ 公式リファレンスでのmatch文の解説
8.6. The match statement
バージョン 3.10 で追加.
The match statement is used for pattern matching. Syntax:引用元 : https://docs.python.org/ja/3/reference/compound_stmts.html#the-match-statement
▼ たとえば match~case文 のコード例
1 2 3 4 5 6 7 8 |
num = 1 match num: case 1: print('One') case 2: print('Two') case _: print('Default') |
嬉しいのはbreak付け忘れリスクがないこと!
見てわかる通り switch num: ~ に相当するのが match num: であり、そのあとはcaseブロックを必要なだけ追加してくだけです。意味不明な break; とか一切いらない
そしてswitch文での default: に相当してるのが case _: という部分ですね。Pythonでは使わないデータはアンダースコア( _ )で表すから、そのルールを継承してるみたいです。
これがPython3.10以降で使えるswitch文の代わり
複数case文に対して同じ処理を実行させるには
上記の例だと1つの値に1つのcase文
でも複数値に対して同じ処理を実行させる場合もあります。
▼ C言語の例 : 次のような記述ができる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
char ch = '1'; switch(ch){ case 1: case '1': printf("One"); break; case 2: case '2': printf("Two"); break; default: printf("Default"); break; } |
このようにの複数のcase文で同じ処理実行。
Pythonでは上記のように複数case文は並列化できません。
代わりに値をパイプ( | )で区切って並列します。
▼ pyコード例 : パイプ( | )で区切って並列に渡す
1 2 3 4 5 6 7 8 |
num = '1' match num: case 1 | '1': print('One') case 2 | '2': print('Two') case _: print('Default') |
case文を並列に書けないので注意です。
それをやると空ブロックでIndentationErrorが出ます。
実際にconda+Python3.10環境を作ってmatch文を使ってみる
このmatch文を早速試してみたくなりました。
手軽ではないけどcondaを使えばすぐに試せます。
※ ここではminicondaインストールしてること前提
▼ python3.10を指定してconda環境を作成
1 |
> conda create -n test_py3.10 python=3.10 |
▼ 今作った仮想環境に切替
1 |
> conda activate test_py3.10 |
▼ 先ほどのmatch文をpyスクリプトを実行
1 2 |
> python test.py One |
できればオンラインで試せたら楽なんだけど…
オンラインコンパイラはv3.10に対応してません。
だからすぐに試したいならcondaとか使ってください。
Python3.9未満ならif文による代替が無難
現在時点ではPython3.10は普及してないと思います。
だからPython3.9以下でswitch文の代替が必要
一番無難なのはif文で代用することです。
▼ 少し冗長だけどこういう書き方で代用
1 2 3 4 5 6 7 |
num = 1 if num == 1: print('One') elif num == 2: print('Two') else: print('Default') |
このコードだとあまり冗長さを感じないけど、分岐が多くなればなるほど num == 1 みたいなのを沢山書くことになります。
あと見やすいものではない気がする
でも無難にやるならif文でも十分です。
lambda配列を使って疑似switch文的なこともできる
それからlambdaによるswitch文の代替について
値とlambdaの配列を作ってswitch文的な処理もできます。
▼ 例えば次のようなコード
1 2 3 4 5 6 7 |
num = 1 { 1: lambda: print('One'), 2: lambda: print('Two') }.get( num, lambda: print('Default') )() |
分岐させたい 値 : lambda式 をオブジェクトとして定義し、getメソッドに値とデフォルトlambda式を渡して返ってきたlambdaを実行という感じです。
やはりmatch~case文と比べて分かりにくい(-_-;)
ただしlambdaには1つ1文という制約がある
あとlambda内には複数文を含められません。
▼ このように書きたいけど許されない
1 2 3 4 5 6 7 8 9 10 11 12 |
num = 1 { 1: lambda: print('hoge') print('One'), ## => SyntaxError 2: lambda: print('fuga') print('Two') }.get( num, lambda: print('Default') )() |
どう頑張ってもlambda式で実行できるのは1行だけ
ググると三項演算子で何とかしてるコードも見つかるけど、ステートメントとしては1行です。複数行はどうやっても含められません。
回避策としては独自関数なり作って渡すくらいです。
それだとif文よりかえって複雑になってしまう……
Python3.9未満だとベストな方法はありません。
結論 match ~ case 文が使えるなら使うべき
Python3.10以降で開発する予定があるなら……
間違いなく match ~ case 文を使った方がいいです。
もし3.9以下なら先ほどの代替策しかありません。
- 少し冗長だけどif文による分岐
- 疑似switch文的なlambda配列
他にも3.10未満で良さげな方法があれば知りたいです。
以上、Python match~case文でした。ではまた