Python でのクロージャとラムダのスコープ分析

WBOY
リリース: 2022-07-22 19:53:30
転載
2472 人が閲覧しました

この記事では、Pythonに関する関連知識を提供し、主にラムダのスコープに関する関連問題と、Python のクロージャーに関する関連コンテンツをまとめています。

Python でのクロージャとラムダのスコープ分析

【関連する推奨事項:Python3 ビデオ チュートリアル

Python クロージャとラムダのスコープ

ラムダの書き方

def fun(): for i in range(3): yield lambda x : x * i f0, f1, f2 = fun() print(f0(1), f1(2), f2(3))
ログイン後にコピー

クロージャの書き方

def fun(): result = [] for i in range(3): def demo(x): return x * i result.append(demo) return result f0, f1, f2 = fun() print(f0(1), f1(2), f2(3))
ログイン後にコピー

上記 2 つの書き方の結果は 2, 4, 6 です。本来の考え方によれば、結果は 0, 2, 6 になるはずです。

問題の原因:

問題の根本は、Python の変数検索ルール LEGB (local、included、global、bultin) にあります。上記の例では、 , i は Closure スコープ (囲み) にあり、Python のクロージャは遅延バインディングであり、クロージャで使用されている変数 i の値は、内部関数が呼び出されたときに見つかります。

解決策

クロージャのスコープをローカルスコープに変更する

ラムダの書き方

def fun(): for i in range(3): yield lambda x, i = i: x * i f0, f1, f2 = fun() print(f0(1), f1(2), f2(3))
ログイン後にコピー

クロージャの書き方

def fun(): result = [] for i in range(3): def demo(x, i=i): return x * i result.append(demo) return result f0, f1, f2 = fun() print(f0(1), f1(2), f2(3))
ログイン後にコピー

上記の出力結果は 0, 2, 6

#別の状況:

def fun(): for i in range(3): yield lambda x : x * i f0, f1, f2 = fun() print(f0(1), f1(2), f2(3))
ログイン後にコピー

出力結果は依然として 2, 4, 6

問題の原因

fun() メソッドによって返されるジェネレーター (またはイテレーター) は実際には実行されませんが、呼び出されるたびに実行されます。

トラバーサル後に印刷が実行される場合、i 変数は最後の呼び出しの値を使用します。ラムダがクロージャ メソッドとみなされる場合、変数 i の値は依然としてクロージャ スコープ内にあります (ローカルではありません)

Python の落とし穴 (クロージャとラムダ)

栗を見てみましょう最初

def create(): return [lambda x:i*x for i in range(5)] for i in create(): print(i(2))
ログイン後にコピー

結果:

8

8

8

8

8

create 関数の戻り値はリストであり、リストの各要素は関数、つまり入力パラメーター x に複数の i を乗算する関数です。予想された結果は 0、2、4、6、8 でしたが、結果は 5 と 8 で、予想外でした。

このトラップが発生する際に lambda がよく使われるため、lambda の問題だと思われるかもしれませんが、lambda は責任を負いたくないという意思表示をしています。問題の本質は、Python の属性検索ルール LEGB (ローカル、エンクロージング、グローバル、ブリティン) にあります。上記の例では、i はクロージャ スコープ (エンクロージング) 内にあり、Python のクロージャは遅延バインディングです。これは、次のことを意味します。内部関数が呼び出されるときに、クロージャで使用される変数の値が照会されます。

解決策も非常に簡単で、クロージャのスコープをローカル スコープに変更するだけです。

def create(): return [lambda x, i=i:i*x for i in range(5)] for i in create(): print(i(2))
ログイン後にコピー

別の書き方:

def create(): a = [] for i in range(5): def demo(x, i=i): return x*i a.append(demo) return a for i in create(): print(i(2))
ログイン後にコピー

上記 2 つの書き方は同じです

結果:

0

2
4
6
8

同様の問題を抱えた別の栗毛##コードは非常に単純です: (ステートメント: python3 質問)

nums = range(2,20) for i in nums: nums = filter(lambda x: x==i or x%i, nums) print(list(nums))
ログイン後にコピー

結果:

[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 , 17, 18, 19]

同様に、通常のロジックによれば、結果は次のようになります:

[2, 3, 5, 7 、11、13、17、19 ]

問題の原因:

Python3 では、filter() 関数はイテレータを返します。したがって、実際には Executed は行われませんが、呼び出されるたびに実行されます (Python2 の filter() によって返される値リストにはこの現象はありません)
  • トラバーサル後に印刷が実行される場合、ループが実行されます, 上の栗と同じです 問題は、変数 i が最後の呼び出しの値を使用することです. 上の栗との違いは、上の栗は埋め込みスコープの値を使用するのに対し、この栗はグローバル i
  • 変更コード:
nums = range(2,20) for i in nums: nums = filter(lambda x,i=i: x==i or x%i, nums) print(list(nums))
ログイン後にコピー

結果:

[2, 3, 5, 7, 11, 13, 17, 19]

【関連する推奨事項:
Python3 ビデオ チュートリアル

]

以上がPython でのクロージャとラムダのスコープ分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:jb51.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!