2011-05-17 27 views
11

Po uruchomieniu poniższego skryptu, zarówno lambda uruchamia os.startfile() na tym samym pliku - junk.txt. Spodziewam się, że każdy lambda użyje wartości "f", która została ustawiona, kiedy lambda została utworzona. Czy istnieje sposób, aby to zadziałało, jak się spodziewam?Zamknięcie Pythona nie działa zgodnie z oczekiwaniami

import os 


def main(): 
    files = [r'C:\_local\test.txt', r'C:\_local\junk.txt'] 
    funcs = [] 
    for f in files: 
     funcs.append(lambda: os.startfile(f)) 
    print funcs 
    funcs[0]() 
    funcs[1]() 


if __name__ == '__main__': 
    main() 
+0

podobne do [generowanie funkcji wewnątrz pętli z wyrażeniem lambda w pythonie] (http://stackoverflow.com/questions/1841268/generating-functions-inside-loop-w-lambda-expression-in-python) – Rodrigue

Odpowiedz

22

Jednym ze sposobów jest, aby to zrobić:

def main(): 
    files = [r'C:\_local\test.txt', r'C:\_local\junk.txt'] 
    funcs = [] 
    for f in files: 
     # create a new lambda and store the current `f` as default to `path` 
     funcs.append(lambda path=f: os.stat(path)) 
    print funcs 

    # calling the lambda without a parameter uses the default value 
    funcs[0]() 
    funcs[1]() 

Inaczej f jest wzrok, gdy funkcja jest wywoływana, więc masz aktualną wartość (po pętli).

Sposoby Lubię lepiej:

def make_statfunc(f): 
    return lambda: os.stat(f) 

for f in files: 
    # pass the current f to another function 
    funcs.append(make_statfunc(f)) 

lub nawet (w Pythonie 2.5+):

from functools import partial 
for f in files: 
    # create a partially applied function 
    funcs.append(partial(os.stat, f)) 
+7

To działa ponieważ domyślne argumenty są powiązane w czasie definicji funkcji. – delnan

+0

Ah, niesamowite. Wygenerowane lambdy są faktycznie używane jako procedury obsługi zdarzeń dla PyQt, więc nie mogą mieć żadnych parametrów wejściowych, a ja aktualnie utknąłem w pythonie 2.4, więc nie mogę używać functools, ale drugie rozwiązanie - przekazuję je do inna funkcja - działa idealnie. Dzięki. –

+0

@Brendan, jeśli definiujesz gniazda qt, powinieneś mieć możliwość wprowadzania parametrów wejściowych z wartościami domyślnymi, nie? Możesz również zastosować dekorator "PyQt4.QtCore.pyqtSlot", jeśli wybierzesz drugie rozwiązanie. –

4

Ważne jest, aby zrozumieć, że gdy zmienna staje się częścią zamknięcia to sama zmienna i nie obejmuje wartości.

Oznacza to, że wszystkie zamknięcia utworzone w pętli używają tej samej zmiennej f, że na końcu pętli będzie zawierała ostatnią wartość użytą w pętli.

Bo w jaki sposób język definiuje jednak te uchwycone zmienne są „tylko do odczytu” w Pythonie 2.x: każde zadanie sprawia zmienną lokalna o ile nie zostanie ogłoszony global (Python 3.x dodaje słowa kluczowego nonlocal pozwalające na piśmie do lokalny z zewnętrznego zakresu).

Jochen Ritzel powiedział w swojej odpowiedzi wspólny idiom, aby uniknąć tej zmiennej wychwytywania i dostać zamiast przechwytywania wartość jest napisać

lambda f=f: os.startfile(f) 

To działa, ponieważ domyślne wartości parametrów są oceniane w funkcji czasu stworzenia, a f jest nie zmienna zewnętrzna, ale parametr funkcyjny, który będzie miał domyślną wartość (więc lambda jest po prostu funkcją z wartościami domyślnymi dla parametrów, nie zamykającą już żadnej zmiennej leksykalnej).

Powiązane problemy