【 Effective Python, 2nd Edition 】Python 3.8 で導入された assignment expression (walrus operator, :=) について 投稿一覧へ戻る
Published 2020年6月12日22:18 by mootaro23
SUPPORT UKRAINE
- Your indifference to the act of cruelty can thrive rogue nations like Russia -
assignment expression (代入書式?代入演算子?) では walrus operator (:=) が使用されます。
直訳すると「セイウチ演算子」。:= がおメメとキバに見えるからとか。
端的に言うと、変数へ値を割り当て、その変数を評価する、という2ステップを1度に済ませる、ということです。
以下、活用場面を見ていきましょう。
今日はレモネードの気分、冷蔵庫の中にレモンがあればレモネードを作りましょう、レモンは1個必要です。。
従来の記述:
count 変数へまず値を割り当て、続けてその値を評価して処理が分岐します。
assignment expression を利用するとこれを端的に表現できます。
アップルジュースにはリンゴが3つ必要です。
従来の記述:
これも assignment expression を利用して表現できます。
ただしこの場合は、代入した結果を比較 (>=3) しその結果で分岐する (if) 、ために、assignment expression を () で囲む必要があります。
ここで、冷蔵庫の中の果物の個数によって、バナナスムージー、アップルジュース、レモネードの順に判断して出してくれる自動ジューサーがあるとします。
バナナスムージーにはバナナが2本必要です。
このようなコードを記述する場合、Python には switch/case ステートメントがありませんから、if/elif/else のディープなネストで表現することが多々生じます。
従来の記述:
こんなときに assignment expression を利用すると、switch/case 文のような見た目の記述にすることができます。
ただコンパクトに収まっただけではなく、ネストが浅い分読みやすさも増しているのではないでしょうか。
もう1つだけ assignment expression が有効な場面を見てみましょう。
冷蔵庫の中の果物を自動的に認識し、瓶詰めジュースを作っておいてくれるマシンがあるとします。
従来の記述 その1:
この実装では、pick_fruit() を2箇所に記述しています。。
1回目は while ループに入るための初期条件をセットするため、2回目は新たなフルーツをセットするため。
この冗長性を排除するために、loop-and-a-half と呼ばれる構文を利用することができます。
従来の記述 その2:
このイディオムで、条件をセットするための pick_fruit() の記述を1回にすることができますが、
while ステートメントがただの無限ループのために利用されているだけで何の条件判断も任されていない、
すべての条件制御が break ステートメントのみによって実現されている、
という点で、あまりエレガントとは言い切れなくなってしまっています。
そこで...
いかがですか?
pick_fruit() からの値の取得、変数へのセット、その値に基づく while ステートメントでの評価が1行で収まっており、
コードの読解性、簡潔性が両立されているのではないでしょうか。
まとめ
直訳すると「セイウチ演算子」。:= がおメメとキバに見えるからとか。
端的に言うと、変数へ値を割り当て、その変数を評価する、という2ステップを1度に済ませる、ということです。
以下、活用場面を見ていきましょう。
今日はレモネードの気分、冷蔵庫の中にレモンがあればレモネードを作りましょう、レモンは1個必要です。。
ref = {
"apple": 10,
"banana": 8,
"lemon": 3,
}
"apple": 10,
"banana": 8,
"lemon": 3,
}
従来の記述:
count = ref.get("lemon", 0)
if count:
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")
if count:
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")
count 変数へまず値を割り当て、続けてその値を評価して処理が分岐します。
assignment expression を利用するとこれを端的に表現できます。
if count := ref.get("lemon", 0):
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")
print("レモネードを作るよ!")
else:
print("あきらめた(泣)")
アップルジュースにはリンゴが3つ必要です。
従来の記述:
count = ref.get("apple", 0)
if count >= 3:
print("アップルジュースを作るよ!")
else:
print("あきらめた(泣)")
if count >= 3:
print("アップルジュースを作るよ!")
else:
print("あきらめた(泣)")
これも assignment expression を利用して表現できます。
ただしこの場合は、代入した結果を比較 (>=3) しその結果で分岐する (if) 、ために、assignment expression を () で囲む必要があります。
if (count := ref.get("apple", 0)) >= 3:
print("アップルジュースを4人分作るよ!")
else:
print("あきらめた(泣)")
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("お買い物に行ってきて...")
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("お買い物に行ってきて...")
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
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()
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)
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)
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 を利用することで同様の機能を今までより簡潔、明白に実現することができる。
この記事に興味のある方は次の記事にも関心を持っているようです...
- People who read this article may also be interested in following articles ... -