はじめに
Effective Python 第二版をよんで、感動した機能などを備忘録を兼ねてまとめておきたいと思います。チョイスは個人的な主観でやっているのでご承知おきください。。
代入式とは?
簡単にいうと、if文で使う変数の宣言を短く記述できるものです。
具体的には、ifの条件式のなかで **:=(セイウチ演算子)**を使って式の評価と変数への代入をひといきに行います。
例えば、フルーツの在庫を表す辞書があるとします。
fruits = {
'apple': 10,
'banana': 8,
'lemon': 5,
}
リンゴジュースをつくるのに少なくとも4つのリンゴが必要な場合、ジュースを実際につくれるかどうかは下のように条件分岐できます。
n_apple = fruits.get('apple', 0)
if n_apple >= 4:
# n_apple個のリンゴでジュースを作る
make_juice(n_apple
else:
# 在庫不足のためジュースを作らない
out_of_stock()
これをセイウチ演算子を使って同じような動作を記述すると、
# リンゴの個数の評価と変数への代入を同時に行う
if (n_apple := fruits.get('apple', 0)) >= 4:
make_juice(n_apple)
else:
out_of_stock()
一見してコードが一行へっただけのように思えますが、このような書き方をすることで、n_appleがifの1つ目のブロックにしか使われないことを明確に表現することができます。
応用例
これだけだと、いまいち良さが理解できないと思うので、よりうれしい例を取り上げてみます。
バナナ→リンゴ→レモンの順番で優先順位をつけて在庫を消化したいとします。また、ジュースをつくるのに最低限必要なフルーツの個数はバナナは2本、リンゴは4個、レモンは1個とします。
おそらく、以下のような入れ子になったif-elseブロックを作るのではないでしょうか。
n_banana = fruits.get('banana', 0)
if n_banana >= 2:
# バナナが2個以上ならバナナジュースを作る
make_juice(n_banana)
else:
n_apple = fruits.get('apple', 0)
if n_apple >= 4:
# リンゴが4個以上ならリンゴジュースを作る
make_juice(n_apple)
else:
n_lemon = fruits.get('lemon', 0)
if n_lemon:
# レモンが1個以上ならレモンジュースを作る
make_juice(n_lemon)
else:
# 在庫不足のためジュースを作らない
out_of_stock()
これはかなり冗長な感じがしますよね。セイウチ演算子を使うと以下のようになります。
if (n_banana := fruits.get('banana', 0)) >= 2:
# バナナが2個以上ならバナナジュースを作る
make_juice(n_banana)
elif (n_apple := fruits.get('apple', 0)) >= 4:
# リンゴが4個以上ならリンゴジュースを作る
make_juice(n_apple)
elif n_lemon = fruits.get('lemon', 0):
# レモンが1個以上ならレモンジュースを作る
make_juice(n_lemon)
else:
# 在庫不足のためジュースを作らない
out_of_stock()
大分よみやすくなりました。
どうですか?これなら使ってみたくなったのではないでしょうか(笑)。
ただし、代入された変数を比較に使う場合には代入式を括弧でくくる必要があることに注意してください。
do-while文的な使い方
もう少し応用的な使い方を紹介します。pythonにはdo/whileループがありませんが、whileブロックとbreak文で同じようなロジックを実装できます。
例えば、在庫の補充とジュースの瓶詰を繰り返しおこなう処理を考えます。在庫の補充がなくなった時点で処理を終了させます。
make_juice()関数は、フルーツの種類と個数を受け取って適切な本数(batch)の瓶づめジュースを返すように変更したとします。
# 瓶を収納するケース
bottles = []
while True:
# フルーツを補充
fruits = pick_fruit()
if not fruits:
# 補充がなければ終了
break
for fruit, count in fruits.items():
# フルーツごとに適切な本数に瓶詰め
batch = make_juice(fruit, count)
# 瓶をケースに追加
bottles.extend(batch)
このような処理も代入式で短く書くことができます。
bottles = []
# 補充したフルーツをfruits変数に代入&空かどうか評価
while fruits := pick_fruit():
for fruit, count in fruits.items():
batch = make_juice(fruit, count)
bottles.extend(batch)
代入文とbreakのためのifブロックが不要になったのがうれしいですね。
おわりに
セイウチ演算子、なかなか便利ですね。python3.8を仕事では使わないけど、この機能は3.7にもあってほしい。