Effective Python CompressionとサードパーティBetterWay 29


コンピュータ(リスト、ディレクトリ、コレクションのいずれか)では、同じ計算を複数の場所で共有することがよくあります.例えば、ある会社は注文を管理するためにプログラムを制定します.
お客様は、新しい注文を発行した後、注文を処理できる在庫があるかどうかを通知します.このため、お客様の要求が在庫数を超えず、配送に必要な最小数(部品数が8個以上でなければならない)を満たすことを確認します.
stock = {
	"못": 125,
    	"나사못": 35,
    	"나비너트": 8,
    	"와셔": 24
}

order = ["나사못", "나비너트", "클립"]

def get_batches(count, size):
	return count // size
    
result = []

for name in order:
	count = stock.get(name, 0)
    	batches = get_batches(count, 8)
        if batches:
        	result[name] = batches
            
print(result)
>>>
{"나사못": 4, "나비너트": 1}
ここで、ディックシャー式を用いると、ループの論理をより簡潔に表現することができる.
found = {name: get_batches(stock.get(name, 0), 8)
このコードは前のコードより短いが、get baches(stock.get(name,0),8)が重複するのが欠点である.そのため、技術的に不要な視覚ノイズが加わり、可読性が悪くなる.また,両方式は常に同じ内容を変更しなければならないため,エラーの可能性も高まる.たとえば、最初のget baches呼び出しでのみ、2番目のパラメータを4ではなく8に変更すると、結果は異なります.
has_bug = {name: get_baches(stock.get(name, 0), 4) 
	for name in order if get_batches(stock.get(name, 0), 8)}
    
print("예상:", found)
print("실제:", has_bug)

>>>
예상: {'나사못': 4, '나비너트': 1}
실제: {'나사못': 8, '나비너트': 2}
これらの問題に対する簡単な解法はPython 3.8に導入されたワーレス演算子(:=)を用いる.ワーレス演算子を使用すると、計算セクションに代入式を作成できます.
found = {name: batches for name in order if (batches := get_batches(stock.get(name, 0), 8))}
バッチ(バッチ:=getバッチ(...)使用すると、stock dickShowneryで各orderキーを1回クエリーし、get batchを1回呼び出して結果をbatch変数に保存できます.comprehensionの他の場所では、getバッチを参照して再呼び出すことなく、ディクソンのコンテンツを作成できます.getバッチを取得する不要な関数呼び出しを削除することで、受注リストの各要素に対して不要な演算を実行することを回避し、パフォーマンスを向上させることができます.
代入式を式の値式に用いても文法的には正しい.ただし、コンパイルされた他の部分から変数を読み込もうとすると、コンパイルの評価順序にエラーが発生します.
result = [name: (tenth := count //10) for name, count in stock.items() if tenth > 0 ]

>>>
Traceback ...
NameError: name 'tenth' is not defined
代入式を条件に移動し、代入式で作成した変数名を計算値式で参照すると、この問題を解決できます.
result = [name: tenth for name, count in stock.items() if (tenth := count // 10 )> 0 ]
計算値セクションでワーレス演算子を使用すると、その値の条件セクションがない場合、ループ変数がループ外領域に漏洩します.
half = [(last := count //2) for count in stock.values()]
print(f'{hlf}의 마지막 원소는 {last}')

>>>
[62, 17, 4, 12]의 마지막 원소는 12
これらのループ変数漏れは,一般的なforループで発生するループ変数漏れと類似している.
for count in stock.values(): # 루프 변수가 누출됨
	pass
print(f'list(stock.values())}의 마지막 원소는 {count}')

>>>
[125, 35, 8, 24]의 마지막 원소는 24
하지만 컴프리헨션의 루프 변수인 경우에는 비슷한 누출이 생기지 않는다. 

# 바로 앞의 예제를 처리하다가 count가 정의된 경에에는 제대로 작동하지 않음
# 파이썬을 재시작하고 아래 코드를 실행해야 오류를 볼 수 있음
half = [count // 2 for count in stock.values()]
print(half) # OK
print(count) # 루프 변수가 누출되지 않기 때문에 예외가 발생

>>>
[]62, 17, 4, 12]
Traceback ...
NameError: name 'count' is not defined
ループ変数を漏らさないほうがいい.したがって,計算では条件の下でのみ代入式を用いることを推奨する.
代入式は第三者の場合も同様に行われる.
次のコードでは、ディック・シリーズのインスタンスではなく、製品名と現在の在庫数のペアからなる小さなバージョンが作成されます.
found = ((name, batches) for name in order 
			 if (batches := get_batches(stock.get(name, 0), 8)))
 print(next(found))
 print(next(found))
 
 >>> 
 ('나사못', 4)
 ('나비너트', 1)
サマリ
  • 代入式により、同一の計算プログラムまたは第三者の条件部分で使用される値を同一の計算プログラムまたは第三者の他の位置で再使用することができる.これにより、可読性とパフォーマンスが向上します.
  • 以外の条件でも代入式を使用できますが、使用は避けてください.