2012-12-25 9 views
5
私は期待しています

Possible Duplicate:
Python lambdas and scopingPythonで関数のリストを定義する

値0、1、2服用3つの定数関数のリスト生じるであろう以下:

lis = [] 
for i in range(3): 
    lis.append(lambda: i) 

しかし、彼らはすべての値を撮って終了します問題を解決するためにディープコピーを期待しますが、それはうまくいかないようです。

+3

これは標準的な質問です。例えばhttp://stackoverflow.com/questions/1107210/python-lambda-problems、http://stackoverflow.com/questions/1924214/python-lambdas-and-scoping。多く、*多く*多く。 –

+0

related:[なぜmap()の結果とリストの理解が異なるのですか?](http://stackoverflow.com/questions/139819/why-results-of-map-and-list-comprelation-are-different) – jfs

答えて

1

あなたのループは、このような何かを書く避けるために:

lis.append(lambda: 0) 
lis.append(lambda: 1) 
lis.append(lambda: 2) 

あなたの意図は一定の整数を返すラムダ関数を記述することです。しかし、オブジェクトiを返す関数を定義して追加しています。従って、3つの付加機能は同じである。あなたが削除した場合

In [28]: lis[0]() 
Out[28]: 2 

戻りi作成した関数の後ろにバイトコード:

In [22]: import dis 
In [25]: dis.dis(lis[0]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE   

In [26]: dis.dis(lis[1]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE   

In [27]: dis.dis(lis[2]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE 

それらの機能のいずれかを呼び出すには、あなたのサンプルコードで2あるiの最新の値を返します。 iオブジェクトの場合、エラーが発生します。

In [29]: del i 

In [30]: lis[0]() 
--------------------------------------------------------------------------- 
NameError         Traceback (most recent call last) 
<ipython-input-30-c9e334d64652> in <module>() 
----> 1 lis[0]() 

<ipython-input-18-15df6d11323a> in <lambda>() 
     1 lis = [] 
     2 for i in range(3): 
----> 3  lis.append(lambda: i) 

NameError: global name 'i' is not defined 

ソリューションは、必要な定数のコードを書いて、実際にそのコードを実行するループを使用して維持することができます。以下の結果を得

In [31]: lis = [] 
    ...: for i in range(3): 
    ...:  exec 'lis.append(lambda: {})'.format(i) 
    ...: 

In [44]: lis[0]() 
Out[44]: 0 

In [45]: lis[1]() 
Out[45]: 1 

In [46]: dis.dis(lis[0]) 
    1   0 LOAD_CONST    1 (0) 
       3 RETURN_VALUE   

In [47]: dis.dis(lis[1]) 
    1   0 LOAD_CONST    1 (1) 
       3 RETURN_VALUE   
+1

診断は正しいです(閉鎖の問題ですが) 'exec'を使用しないでください!それは怖い!悪の!不快な!不快! –

+0

@ChrisMorganここで私の唯一の目標は、何度も何度も戻ってくるラムダクロージャを説明しようとすることです。ここで 'exec'は、期待されるソースコードのメタレベル(OPの意図)を示すことです。 @BasicWolfのようにラムダのデフォルトの引数を使うのは説明の外に出る方法だと私は同意します。 – Boud

+0

@ChrisMorganここで唯一の解決策はexecを使用しています。より良い提案がありますか? – kilojoules

3

@ Boudは、あなたのコードが期待通りに動作しない理由を説明する非常に良い答えを出しました。明らかにラムダで参照される前に、iの値を評価する必要があります。ここでそれを行うには少しハック方法です:

lis = [] 
for i in range(3): 
    lis.append(lambda i=i: i) 

これは、Pythonのデフォルトの関数引数の値は、例えば、特色使用しています関数の中で次のように記述します。

def f(i=10): 
    return i 

今、トリックは、関数(メソッド、ラムダ式)が作成されたときに引数がポイントに保存されたデフォルト値を持っているということです。したがって:

j = 10 
def f(i=j): 
    return i 

j = 20 
print(f(125)) # no matter that j has been changed, the output is... 
>>> 125 

また、同じトリックがラムダに適用されます。

+0

実際、デフォルトの引数を使用することは、私は見た。 –

関連する問題