Pythonにおけるクロージャ(closure)は、外部変数の値を「記憶」する関数であり、たとえそれがスコープの外で呼び出されても、その値を保持します。クロージャは、内部関数が外部関数で定義された変数を参照するときに作成されます。Python 3では、ネストされた関数からこれらの変数を変更するために、nonlocalキーワードを使用します。
例:
def make_accumulator(): total = 0 def add(value): nonlocal total total += value return total return add acc = make_accumulator() print(acc(10)) # 10 print(acc(5)) # 15
nonlocalがないと、変数totalは変更できない(UnboundLocalErrorエラー)。
質問: 「内部関数からnonlocalなしで外部関数の変数を増加させることはできますか?」
回答: いいえ、nonlocalなしでは、エラーが発生します。なぜなら、インタプリタはその変数を内部関数のローカルと見なすからです。例:
def outer(): count = 0 def inner(): count += 1 # UnboundLocalErrorエラー return count return inner
物語 あるウェブアプリケーションで、ネストされた関数を通じて各ハンドラのローカルカウンタを実装しようとしましたが、
nonlocalを使用するのを忘れました。その結果、カウンタは常に1を返し、外部クロージャとは無関係なローカル空間でcountをカウントしていました。
物語 マルチスレッドの問題では、クロージャやラムダを頻繁に使用します。クロージャを理解していなかったため、すべてのラムダ関数がループ変数の最後の値を「記憶」し、タスクを異なるものとして処理する代わりに互いにコピーしました。
物語 デコレーターを正しいクロージャなしで書いたため(外部スコープにパラメータを渡さなかった)、デコレーターが呼び出し間で状態を保持できないという状況が発生し、予期しない結果とアプリケーションの不安定な動作を引き起こしました。