cover_effective_python

【 Effective Python, 2nd Edition 】Python 3.8 で導入された assignment expression (walrus operator, :=) について 投稿一覧へ戻る

Tags: Python , Effective , assignment , operator , walrus , expression

Published 2020年6月12日22:18 by T.Tsuyoshi

assignment expression (代入書式?代入演算子?) では walrus operator (:=) が使用されます。
直訳すると「セイウチ演算子」。:= がおメメとキバに見えるからとか。


端的に言うと、変数へ値を割り当て、その変数を評価する、という2ステップを1度に済ませる、ということです。


以下、活用場面を見ていきましょう。


今日はレモネードの気分、冷蔵庫の中にレモンがあればレモネードを作りましょう、レモンは1個必要です。。


ref = {
"apple": 10,
"banana": 8,
"lemon": 3,
}



従来の記述:
count = ref.get("lemon", 0)
if count:
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")



count 変数へまず値を割り当て、続けてその値を評価して処理が分岐します。
assignment expression を利用するとこれを端的に表現できます。


if count := ref.get("lemon", 0):
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")



アップルジュースにはリンゴが3つ必要です。


従来の記述:
count = ref.get("apple", 0)
if count >= 3:
print("アップルジュースを作るよ!")
else:
print("あきらめた(泣)")



これも assignment expression を利用して表現できます。
ただしこの場合は、代入した結果を比較 (>=3) しその結果で分岐する (if) 、ために、assignment expression を () で囲む必要があります。


if (count := ref.get("apple", 0)) >= 3:
print("アップルジュースを4人分作るよ!")
else:
print("あきらめた(泣)")



ここで、冷蔵庫の中の果物の個数によって、バナナスムージー、アップルジュース、レモネードの順に判断して出してくれる自動ジューサーがあるとします。


バナナスムージーにはバナナが2本必要です。


このようなコードを記述する場合、Python には switch/case ステートメントがありませんから、if/elif/else のディープなネストで表現することが多々生じます。


従来の記述:
count = ref.get("banana", 0)
if count >= 2:
print("バナナスムージーでエナジー満タン!")
else:
count = ref.get("apple", 0)
if count >= 3:
print("アップルジュースで医者要らず!")
else:
count = ref.get("lemon", 0)
if count:
print("レモネードでスッキリさわやか!")
else:
print("お買い物に行ってきて...")



こんなときに assignment expression を利用すると、switch/case 文のような見た目の記述にすることができます。


if (count := ref.get("banana", 0)) >= 2:
print("バナナスムージーでエナジー満タン!")
elif (count := ref.get("apple", 0)) >= 3:
print("アップルジュースで医者要らず!")
elif count := ref.get("lemon", 0):
print("レモネードでスッキリさわやか!")
else:
print("お買い物に行ってきて...")



ただコンパクトに収まっただけではなく、ネストが浅い分読みやすさも増しているのではないでしょうか。


もう1つだけ assignment expression が有効な場面を見てみましょう。


冷蔵庫の中の果物を自動的に認識し、瓶詰めジュースを作っておいてくれるマシンがあるとします。


def pick_fruit() -> dict:
pass

def make_bottled_juice(fruit, count):
pass



従来の記述 その1:
bottles = []
fresh_fruit = pick_fruit()
while fresh_fruit:
for fruit, count in fresh_fruit.items():
bottled_juice = make_bottled_juice(fruit, count)
bottles.extend(bottled_juice)
fresh_fruit = pick_fruit()



この実装では、pick_fruit() を2箇所に記述しています。。


1回目は while ループに入るための初期条件をセットするため、2回目は新たなフルーツをセットするため。


この冗長性を排除するために、loop-and-a-half と呼ばれる構文を利用することができます。


従来の記述 その2:
bottles = []
while True: # Loop
fresh_fruit = pick_fruit()
if not fresh_fruit: # And a half
break

for fruit, count in fresh_fruit.items():
bottled_juice = make_bottled_juice(fruit, count)
bottles.extend(bottled_juice)



このイディオムで、条件をセットするための pick_fruit() の記述を1回にすることができますが、


while ステートメントがただの無限ループのために利用されているだけで何の条件判断も任されていない、


すべての条件制御が break ステートメントのみによって実現されている、


という点で、あまりエレガントとは言い切れなくなってしまっています。


そこで...


bottles = []
while fresh_fruit := pick_fruit():
for fruit, count in fresh_fruit.items():
bottled_juice = make_bottled_juice(fruit, count)
bottles.extend(bottled_juice)



いかがですか?


pick_fruit() からの値の取得、変数へのセット、その値に基づく while ステートメントでの評価が1行で収まっており、
コードの読解性、簡潔性が両立されているのではないでしょうか。


まとめ

* Assignment expression は walrus operator (:=) を利用し、変数への値の代入/評価を1行で行えるため、冗長性を削除できる。

* Assignment expression が長い書式中のサブ書式として含まれている場合は ( ) で囲む必要がある。

* Python では switch/case、do/while 構文は利用できないが、assignment expression を利用することで同様の機能を今までより簡潔、明白に実現することができる。

この投稿をメールでシェアする

0 comments

コメントはまだありません。

コメントを追加する(不適切と思われるコメントは削除する場合があります)