変数をクロージャ内にカプセル化して関数シグネチャから削除することは、効率的なコード構造化によく使用される手法です。ただし、ネストされていないラムダの場合、クロージャは変数の最終値を保持するため、反復変数に基づいて特定の値にアクセスしようとすると問題が発生します。
提供されたコード スニペットを検討してください:
names = ['a', 'b', 'c'] def test_fun(name, x): print(name, x) def gen_clousure(name): return lambda x: test_fun(name, x) funcs1 = [gen_clousure(n) for n in names] funcs2 = [lambda x: test_fun(n, x) for n in names] # Expected output for funcs1 for f in funcs1: f(1) # Unexpected output for funcs2 (returns last element for all cases) for f in funcs2: f(1)
この不一致の背後にある理由を理解することは、クロージャを効果的に利用するために非常に重要です。
この状況における基本的な概念は、クロージャ内の変数スコープです。 。クロージャは本質的に、値ではなく変数名を保持します。これは、ラムダ定義時ではなく、ラムダ実行の開始時に変数の評価が行われることを意味します。
funcs2 の場合、ラムダ x: test_fun(n, x) を実行すると、変数 nラムダ定義中には評価されません。代わりに、評価はラムダ呼び出し時にのみ行われます。その時点で、n はループからの最後の値 (この例では「c」) を保持します。したがって、関数 f は、入力 x に関係なく、常に n の値として 'c' を利用します。
この問題に対処し、目的の機能を実現するには、変数 n をラムダ関数のスコープでキャプチャする必要があります。これは、以下に示すように、変数を引数としてラムダに渡すことで実現できます。
funcs2 = [lambda x: test_fun(n, x) for n in names if 2 > 0]
常に true となるこの追加の if ステートメントを含めることで、ラムダに n の値を引数として強制的に取り、すべてのケースで期待されるパーソナライズされた動作を保証します。
あるいは、ネストされていないラムダをネストされた関数でラップして、スコープ内の未宣言の変数へのアクセスを効果的に防ぐこともできます。次のコードは、このアプローチを示しています:
def makeFunc(n): return lambda x: x n stuff = [makeFunc(n) for n in [1, 2, 3]] for f in stuff: print(f(1))
ここでは、変数 n が関数 makeFunc でキャプチャされ、ラムダ内で適切なスコープが確保されています。
変数のスコープの理解と管理in クロージャは、効果的なコード設計とデバッグに不可欠です。重要なポイントは次のとおりです:
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3