<ショートコーディング>イケてる書き方5選!【Python】

Python

Pythonは短く書いてなんぼです。
個人的にPythonでイケてる!と思う書き方を5つご紹介します。

リスト内包表記

square_result = [ num**2  for num in range(10) ]
#                 ^(1)^^  ^^^^^^^^(2)^^^^^^^^^
range(10)     : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
square_result : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

(1)がリストに入る要素の値を表しています。
(2)がどのようなリストをどのように繰り返すか、を表しています。
ここは通常のfor文の、for ~~~ in ~~~ の記述と同一の意味になります。

if句付き

square_result = [ num**2  for num in range(10)  if num % 2 == 0 ]
#                 ^(1)^^  ^^^^^^^^(2)^^^^^^^^^  ^^^^^^(3)^^^^^^
range(10)     : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
square_result : [0, 4, 16, 36, 64]

(3)はif句と呼ばれ、リストをフィルタリングできます。
上記の例では、リストを偶数の要素のみにフィルタリングしています。
ちなみに以下とは意味が異なるので注意です。

square_result = [ num**2 if num % 2 == 0 else -1  for num in range(10) ]
#                 ^^^^^^^^^^^^^^(1)^^^^^^^^^^^^^  ^^^^^^^^(2)^^^^^^^^^
range(10)     : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
square_result : [0, -1, 4, -1, 16, -1, 36, -1, 64, -1]

上記は(1)の要素の値を入れる箇所に、三項演算子を入れています。(三項演算子は値)
if句ではないのでフィルタリングされるわけではなく、各要素に対し条件分岐が走ります。
上記の例では、もともと偶数だった要素については2乗の計算をし、そうでない要素については「-1」を入れています。

実用例

以下は、名前のリストをイニシャルのリストに変更するコードです。
実際にありそうなものを適当に作ってみました。
追いかけて意味を理解してみてください。

names = ["Shohei Otani", "Yu Darvish", "Lars Taylor-Tatsuji Nootbaar"]

split_names = [name.split() for name in names]

initials = [".".join([name[0] for name in split_name]) for split_name in split_names]
['S.O', 'Y.D', 'L.T.N']

アンパック

name, weight, height = "Temmaru", 65, 175

print(f'私は{name}です。体重は{weight}kg、身長は{height}cmです。')
私はTemmaruです。体重は65kg、身長は175cmです。

上のように、カンマつなぎで複数の変数に対して一度に代入することが可能です。
そして、以下のようなこともできます。

name, *hobby, blood  = "Temmaru", "麻雀", "競馬", "パチンコ", "A型"

print(f'私は{name}です。趣味は{"、".join(hobby)}で、血液型は{blood}です。')
私はTemmaruです。趣味は麻雀、競馬、パチンコで、血液型はA型です。

*で指定した変数が、要素数の整合性の取れない部分をリスト化してまとめてくれます。
アンパックはめちゃくちゃ便利なのと、実際に開発を始めると頻出します。

※この後の他の章でも頻出します。

enumerate

リストの各要素を「インデックス」と「値」の組としてまとめて、新しいイテレータを作成する関数です。
主にforループにおいて、ループしたいリストのインデックスと値を一度に取得するために使われます。
以下に例を挙げます。

batting_order = ["ヌー", "こんどー", "オオタニサン"]

#   ↓ ↓ ↓ ↓ ※アンパックです
for no, name in enumerate(batting_order):
    print(f'バッター{no +1}番、{name}、{name}です。')
バッター1番、ヌー、ヌーです。
バッター2番、こんどー、こんどーです。
バッター3番、オオタニサン、オオタニサンです。

forループ内で、インデックスと値の両方使いたい、というケースは実際にめちゃくちゃあると思います。
インデックス変数の定義やインクリメント処理などが必要になったりしますが、Pythonではこれで一発です。
(とはいえ、enumerate系は最近の言語ではほとんどあります。)

zip

2つ以上のリストについて、各リストの対応するインデックスの値を1つの組としてまとめて、新しいイテレータを作成する関数です。
そんなに難しいものではないので、ちょっと複雑な例を以下に挙げます。
ショートコーディングを意識して書いています。

batting_ave = [("ヌー", 0.429), ("こんどー", 0.467), ("オオタニサン", 0.5)]
# 打席1番~、名前と打率のタプル

expected_batting_ave = [0.25, 0.20, 0.30] # 期待打率(超テキトー)

#   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ※アンパックです
for (name, ave), expected_ave in zip(batting_ave, expected_batting_ave):
    print(f'{name}は、{"最高" if ave >= expected_ave else "不調"}です')
ヌーは、最高です
こんどーは、最高です
オオタニサンは、最高です

これも超便利です。
相関があるけど独立性を意識して作られている配列群について、相関関係を意識して使いたいときに1行でまとめることができます。
これも他の言語で書こうとすると、多分3行以上増えますよね。

with

コンテキストマネージャについて、try~catch~finallyを書かなくても、終了処理をいい感じにしてくれます。
よくファイルオープン/クローズが挙げられますが、コンテキストマネージャが対象となります。
コンテキストマネージャとは何か、を簡単にいうとtry~catch~finally句で確保/解放が必要な処理諸々だと思ってもらえると、分かりやすいと思います。
例えば「test.txt」についてファイル操作する場合、以下のように書くのみです。

with open("test.txt", "w") as f
    # ファイル操作に関する処理(f.read, f.writeとか)

特にcloseする必要はありません。
withによって自動でしてくれます。

ファイル操作やDB操作でよく使いますが、もしあなたがopenやcloseが必要な処理を記載しようと思ったとき、まずはそれがwithの対象かどうかを調べるのがいいかもしれません。

最後に

Pythonはどの言語と比べても、様々なケースにおいて非常に短くかける言語です。
C言語、Java、JavaScript等、メジャーどころの色々な言語に触れてきた私からすると、本当にそう感じます。
もしあなたがPythonで開発を始めて、実装したい内容に対し、若干記載量が多いかなと感じたら、それは疑ってみてください。
そして調べてみてください。
きっと、もっと短く実装する方法があるでしょう。

勿論、短く書ければいいわけでなく、「分かりやすさ」を保ったまま短く書く、がベストです。
ですが、どうすれば短く書けるかを知っておかないとそれもできません。
短く書く方法について学習し、イケてるコードを書けるようにしておきましょう!

コメント

タイトルとURLをコピーしました