2012-01-20 15 views
18

Ho appena incontrato il numero Closing over the loop variable considered harmful di Eric Lippert tramite SO e, dopo aver sperimentato, mi sono reso conto che lo stesso problema esiste (ed è ancora più difficile da aggirare) in Python.Esiste un modo Pythonic per chiudere su una variabile di loop?

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

e, il C# soluzione standard non funziona (presumo a causa della natura dei riferimenti in 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 

riconosco che questo non è un gran problema in Python con la sua enfasi generale sulle strutture di oggetti non a chiusura, ma sono curioso di sapere se esiste un ovvio modo Pythoniano per gestirle, o dobbiamo seguire la rotta JS delle chiamate di funzioni annidate per creare varsene realmente nuove?

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

risposta

23

Un modo è quello di utilizzare un parametro con il valore predefinito:

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

for f in l: 
    print(f()) 

cede

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 

Questo funziona perché definisce un r in ambito locale foo s', e lega il valore predefinito ad esso al momento è definito foo.


Un altro modo è quello di utilizzare una fabbrica funzione:

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()) 

Questo funziona perché definisce r in ambito locale make_foo s', e si lega un valore ad essa quando make_foo(r) si chiama. Successivamente, quando viene chiamato f(), viene cercato r utilizzando lo LEGB rule. Sebbene r non sia stato trovato nell'ambito locale di foo, si trova nell'ambito di applicazione di make_foo.

+1

Oh, mi piace il trucco di argomento predefinito. Quella fabbrica di funzioni è semanticamente simile alla doppia cosa lambda alla fine della mia. Le fabbriche Lambda sono la rovina della leggibilità, però. – quodlibetor

Problemi correlati