PythonはマジでSwitch文が無い
仕事などでちょくちょくPythonのスクリプトを書くのですが、先日、今までPythonをやってて一番衝撃的な事実が…。
「PythonはSwitch文無ぇから、やりたかったらif文連ねやがれ」
普段はShellscriptorであり、VBAerなわたくしがSwitch!Switch!言うのもおかしいですが(CaseとSelect...)、
いやいや、いくら何でもif文列挙しまくるのって見た目綺麗じゃないよね?!と思い、代替方法を考えた次第。
実装したい処理その1 - 文字列単純比較で条件分岐
では、お題に沿ってbashのケースとPythonのケースを考えてみましょう。
まず、ダメ外人の名前を入力すると、フルネームで答えてくれるスクリプトを作ります。
bashでいうところの以下のような感じ。
case ${player} in
"kinkade")
echo "Michael Arthur Kinkade"
;;
"mench")
echo "Kevin Ford Mench"
;;
"conrad")
echo "Brooks Litchfield Conrad"
;;
*)
echo "Marcos Vechionacci"
;;
esac
これをPythonのifでいくとすると以下。
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")
フローは分かり易いんですが、なんか文字ごちゃごちゃしてるし無骨な印象を受けます。
いや待てよ**「キーワードに対してそれに紐づく値を返す」**って、やりたいことがまんま辞書ではないか!ということで
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...」「キンケード!!」と答えてくれた方がありがたいですよね。
シェルスクリプトは地味に優秀なので、条件にワイルドカード等が使えます。
case $player in
k*)
echo "Michael Arthur Kinkade"
;;
m*)
echo "Kevin Ford Mench"
;;
c*)
echo "Brooks Litchfield Conrad"
;;
*)
echo "Marcos Vechionacci"
;;
esac
Pythonの辞書のキーにワイルドカードは使えませんので、これは考えねばなりません。
ということで、正規表現対応版は以下。
なお、コメントで辞書の順序性について言及がありましたので、OrderedDictを使うようにしました。
コメントありがとうございました!
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番を返すことで、疑似的に最初にマッチしたものを取り出すようになってます。
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))