変数をクロージャ内にカプセル化して関数シグネチャから削除することは、効率的なコード構造化によく使用される手法です。ただし、ネストされていないラムダの場合、クロージャは変数の最終値を保持するため、反復変数に基づいて特定の値にアクセスしようとすると問題が発生します。
提供されたコード スニペットを検討してください:
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 でキャプチャされ、ラムダ内で適切なスコープが確保されています。
理解とクロージャで変数のスコープを管理することは、効果的なコード設計とデバッグに不可欠です。重要なポイントは次のとおりです:
以上が入れ子になっていないラムダクロージャで変数のスコープの問題を処理するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。