2016-05-17 10 views
2

で同じ結果を返しません。一覧や発電機は、Python

def create_funcs(): 
    return (lambda: i for i in range(5)) 

for f in create_funcs(): 
    print(f()) 

は私が期待し得る:

1 
2 
3 
4 

しかし、私は実行します。

def create_funcs(): 
    return [lambda: i for i in range(5)] 

for f in create_funcs(): 
    print(f()) 

私は変だ:

4 
4 
4 
4 

誰も理由を説明できますか?

+0

@BrenBarn:申し訳ありませんが、他の質問の答えが私のものであるかどうか分かりません。 –

+0

特に[この回答](http://stackoverflow.com/questions/4575698/python-list-comprehension-overridingvalue/4575866#4575866)をご覧ください。 – BrenBarn

+0

@BrenBarn:OKですが、私はPython3.5を使用しています。なぜ漏れてしまいますか? –

答えて

5

lambda関数は、変数iを周囲のスコープから取得します。 this questionに記載されているように、その範囲は理解の範囲である。

ループはジェネレータのアイテムを一度に1つずつ繰り返し、ジェネレータを進める前にループを呼び出します。最初の関数を取得すると、ジェネレータは、iが0の状態で中断されているので、関数が参照する値になります。ジェネレータを進めると、iが1に設定され、その値を取得する関数が得られます。ジェネレータは、次の機能に進むまで、iを変更するのを待ちます。

リストの理解では、すぐにすべての関数を取得します。しかし、すべてはまだ理解範囲内の同じ変数iを参照しています。リストの理解はすぐに終了するので、iは4つまですべての機能を使用する機会を得ます。

どちらの場合でも、ラムダは理解範囲内の変数を参照しています。ジェネレータの理解では、以前の関数を呼び出した後でなければ、この変数は進まないということだけです。あなたが最初の関数を保存する場合は、リスト・理解のケースと同様の挙動を見ることができますが、再び発電進出後までそれを呼び出すことはありません:

def create_funcs(): 
    return (lambda: i for i in range(5)) 

gen = create_funcs() 
f1 = next(gen) 
f2 = next(gen) 
print(f1()) 
print(f2()) 

出力があるので

1 
1 

です私は発電機を進めました、iは1なので、f1f2の両方が呼び出されたときにその値を参照してください。 for f in list(create_funcs())を実行しても同じことが分かります。リストの理解が終わるまでジェネレータを最後まで強制的に移動させます。

+0

を参照してください。私が 'lambda:i'を' lambda:int(i) 'に置き換えると、私は私がもう私を直接参照していないので動作します。お待ち頂きまして、ありがとうございます。 –

+0

@JacquesGaudin: 'int(i)'を使うと何も変更してはいけません。 – BrenBarn

+0

'lambda:int(i)'は現在のコードとまったく同じように動作するはずです。問題を回避するには、 'lambda i = i:i'を使います。これは、ラムダが作成された時点で 'i'の値をラムダ自身の名前空間のデフォルト値にバインドします。 – Blckknght

-1

BrenBarnにも言及されているように、リスト内の変数iは、理解の範囲にローカルです。

def create_funcs(): 
    return (lambda: i for i in range(5)) 

for f in create_funcs(): 
    print(f()) 

0 
1 
2 
3 
4 
+1

彼はすでに彼の質問の最初の例でそれを行います。彼の質問は、2つのケースで行動が異なる理由です。 – BrenBarn