13

私は次の簡単なコードを持っています:

def get():
    return [lambda: i for i in [1, 2, 3]]

for f in get():
    print(f())

私のPythonの知識から予想されるように、出力は3-リスト全体に最後の値が含まれますi。しかし、これは内部でどのように機能しますか?

AFAIK、Python変数は単にオブジェクトへの参照であるため、最初のクロージャーはオブジェクトの最初のi参照を囲む必要があります-このオブジェクトは間違いなく1であり、3O_Oではありません。Pythonクロージャが、この変数参照のオブジェクトではなく、変数自体を囲むのはどうしてですか?変数名をプレーンテキスト、「変数への参照」などとして保存しますか?

4

3 に答える 3

13

@ thg435が指摘しているように、ラムダはその時点で値をカプセル化するのではなく、スコープをカプセル化します。これに対処する方法は小さすぎます。

ラムダのデフォルト引数「ハック」

[ lambda v=i: v for i in [ 1, 2, 3 ] ]

またはfunctools.partialを使用します

from functools import partial
[ partial(lambda v: v, i) for i in [ 1, 2, 3 ] ]

基本的に、作成している関数に対してローカルになるようにスコープを移動する必要があります。一般的にpartial、呼び出し可能オブジェクトと、適切なクロージャを持つ呼び出し可能オブジェクトを作成するための任意のargsとkargsを渡すことができるため、より頻繁に使用するのが好きです。内部的には、元の呼び出し可能オブジェクトをラップしているため、スコープがシフトされます。

于 2012-06-19T21:45:34.793 に答える
10

クロージャは変数を参照するのではなく、スコープを参照します。iスコープ内のの最後の値は「3」であるため、3つのクロージャーすべてが同じ値を返します。変数の現在の値を「ロック」するには、その変数専用の新しいスコープを作成します。

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )
于 2012-06-19T21:41:06.880 に答える
4

それぞれlambdaは実際には同じを参照してiいます。これはリスト内包によって作成された変数です。リスト内包表記が終了するとi、スコープから外れるまで、割り当てられた最後の要素の値を維持します(これは、関数内にカプセル化して返すことで防止されますlambda)。他の人が指摘しているように、クロージャは値のコピーを維持しませんが、スコープ内で定義された変数への参照を維持します。

于 2012-06-19T21:46:51.503 に答える