61
59

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonにswitch文無いんですか!?

Last updated at Posted at 2017-10-21

PythonはマジでSwitch文が無い

仕事などでちょくちょくPythonのスクリプトを書くのですが、先日、今までPythonをやってて一番衝撃的な事実が…。

「PythonはSwitch文無ぇから、やりたかったらif文連ねやがれ」

普段はShellscriptorであり、VBAerなわたくしがSwitch!Switch!言うのもおかしいですが(CaseとSelect...)、
いやいや、いくら何でもif文列挙しまくるのって見た目綺麗じゃないよね?!と思い、代替方法を考えた次第。

実装したい処理その1 - 文字列単純比較で条件分岐

では、お題に沿ってbashのケースとPythonのケースを考えてみましょう。
まず、ダメ外人の名前を入力すると、フルネームで答えてくれるスクリプトを作ります。
bashでいうところの以下のような感じ。

suketto.sh
case ${player} in
    "kinkade")
        echo "Michael Arthur Kinkade"
        ;;
    "mench")
        echo "Kevin Ford Mench"
        ;;
    "conrad")
        echo "Brooks Litchfield Conrad"
        ;;
    *)
        echo "Marcos Vechionacci"
        ;;
esac

これをPythonのifでいくとすると以下。

suketto.py
if player is 'kinkade':
    print("Michael Arthur Kinkade")
elif player is 'mench':
    print("Kevin Ford Mench")
elif player is 'conrad':
    print("Brooks Litchfield Conrad")
else:
    print("Marcos Vechionacci")

フローは分かり易いんですが、なんか文字ごちゃごちゃしてるし無骨な印象を受けます。

いや待てよ**「キーワードに対してそれに紐づく値を返す」**って、やりたいことがまんま辞書ではないか!ということで

suketto2.py
suketto = {
    "kinkade": "Michael Arthur Kinkade",
    "mench":   "Kevin Ford Mench",
    "conrad":  "Brooks Litchfield Conrad"
}
dame_gaijin = suketto.get(player,"Marcos Vechionacci")

get()は辞書の中から第一引数のキーを探し、ある場合はそのキーに紐づく値、無い場合は第二引数を返す関数です。
辞書型でやるとすっきり見える気がします。処理部分はワンライナーです。でもベキオナチが若干浮いてる。

実装したい処理その2 - 文字列曖昧比較で分岐

実装1ではダメ外人の名前を入れたらフルネームで答えてくれましたが、ダメ外人の名前が出てこない可能性だってあります。
「あいつなんやっけ、k...k...」「キンケード!!」と答えてくれた方がありがたいですよね。

シェルスクリプトは地味に優秀なので、条件にワイルドカード等が使えます。

suketto_z.sh
case $player in
    k*)
        echo "Michael Arthur Kinkade"
        ;;
    m*)
        echo "Kevin Ford Mench"
        ;;
    c*)
        echo "Brooks Litchfield Conrad"
        ;;
    *)
        echo "Marcos Vechionacci"
        ;;
esac

Pythonの辞書のキーにワイルドカードは使えませんので、これは考えねばなりません。
ということで、正規表現対応版は以下。
なお、コメントで辞書の順序性について言及がありましたので、OrderedDictを使うようにしました。
コメントありがとうございました!

suketto_z.py
import re
from collections import OrderedDict
def switch(player):

    suketto = OrderedDict((
        ("c.*", "Brooks Litchfield Conrad"),
        ("k.*", 'Michael Arthur Kinkade'),
        ("m.*", "Kevin Ford Mench"),
        (".*",  "Marcos Vechionacci")
    ))
  
    for k,v in suketto.items():
        if re.search(k,player):
            return v

dame_gaijin = switch('c')

もはやswitchという関数を作って中でごちゃごちゃやるしかなくなりました。
辞書のキーに正規表現用の文字列を準備し、それをループして与えられた選手名と比べ、
マッチした時点でreturnしてフルネームを返してます。(ループ中のreturnは超いまいちですが。)
想定しないものが引っ掛からないようにするためには、正規表現を頑張らないといけませんね。

なお、lambdaとかで強引に圧縮したものは以下になります。
リストの0番を返すことで、疑似的に最初にマッチしたものを取り出すようになってます。

suketto_z2.py
import re
from collections import OrderedDict
suketto = OrderedDict((
    ("c.*", "Brooks Litchfield Conrad"),
    ("k.*", 'Michael Arthur Kinkade'),
    ("m.*", "Kevin Ford Mench"),
    (".*",  "Marcos Vechionacci")
))
dame_gaijin = (lambda player: [v for k,v in suketto.items() if re.search(k,player)][0])(player)

またもやワンライナーでいけました。いとやんごとなし。

PythonはSwitch文無いけど工夫次第

結局のところ、PythonではSwitch文が無くてもそこそこわかり易い分岐が書けました。
だからこそ、Switch文は実装されていないんでしょうね。
にしても、正規表現での分岐が一行でかけるとか、Pythonはシェル芸人の為にあるんでしょうか…。
え?可読性?食べたことないです(笑)

追記:
@shiracamus さんにコメントでご教示頂いたコードが今のところ一番スッキリしてますね。奥深…。

import re
suketto = (
    ("c.*", "Brooks Litchfield Conrad"),
    ("k.*", 'Michael Arthur Kinkade'),
    ("m.*", "Kevin Ford Mench"),
    (".*",  "Marcos Vechionacci")
)
dame_gaijin = lambda player: next(v for k,v in suketto if re.match(k,player))
61
59
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
61
59

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?