2012-01-20 12 views
18

私はちょうどSO経由でEric LippertのClosing over the loop variable considered harmfulを実行しました。実験の後、Pythonで同じ問題が存在することがわかりました。ループ変数を閉じるためのPythonの方法はありますか?

私はこれはとPythonでの問題の多くはないことを認識し
>>> l = [] 
>>> for r in range(10): 
... def foo(): 
...  return r 
... l.append(foo) 
... 
>>> for f in l: 
... f() 
... 
9 
9 
9 
# etc 

と、標準のC#の回避策は動作しません(私が原因Pythonでの参照の性質の仮定)

>>> l = [] 
>>> for r in range(10): 
... r2 = r 
... def foo(): 
...  return r2 
... l.append(foo) 
... 
>>> for f in l: 
... f() 
... 
9 
9 
9 
# etc 

非閉包オブジェクト構造に一般的に重点を置いていますが、これを処理する明白なPythonの方法があるのか​​不思議ですか、実際に新しい変数を作成するためにJSのネストされた関数呼び出しのルートに移動しなければなりませんか?

>>> l = [] 
>>> for r in range(10): 
...  l.append((lambda x: lambda: x)(r)) 
... 
>>> for f in l: 
...  f() 
... 
0 
1 
2 
# etc 

答えて

23

一つの方法として、デフォルト値でパラメータを使用することです:

l = [] 
for r in range(10): 
    def foo(r = r): 
     return r 
    l.append(foo) 

for f in l: 
    print(f()) 

利回り

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 

それはfooのローカルスコープでrを定義し、バインドするので、これは動作しますfooが定義されているときのデフォルト値。


もう一つの方法は、関数ファクトリを使用することです:

l = [] 
for r in range(10): 
    def make_foo(r): 
     def foo(): 
      return r 
     return foo 
    l.append(make_foo(r)) 

for f in l: 
    print(f()) 

それはmake_fooのローカルスコープでrを定義し、make_foo(r)が呼び出されたときに値をバインドするので、これは動作します。その後、f()が呼び出されると、LEGB ruleを使用してrがルックアップされます。 rはローカルスコープfooには見つかりませんが、囲みの範囲make_fooにあります。

+1

ああ、私はそのデフォルトの引数が好きです。そのファンクションファクトリは、私の終わりの二重ラムダと意味的に似ています。しかし、ラムダ工場は読みやすさの欠点です。 – quodlibetor

関連する問題