2011-10-28 8 views
38

Załóżmy, że mam następujący kod Pythona:Python nadpisywanie zmiennych w funkcji zagnieżdżonych

def outer(): 
    string = "" 
    def inner(): 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

Chcę wywołanie zewnętrznej(), aby wrócić! „String został zmieniony przez zagnieżdżonej funkcji”, ale pojawia się „” . Wnioskuję, że Python uważa, że ​​linia string = "string was changed by a nested function!" jest deklaracją nowej zmiennej lokalnej do wewnętrznej(). Moje pytanie brzmi: jak mogę powiedzieć Pythonowi, że powinien używać zewnętrznego() ciągu? Nie mogę użyć słowa kluczowego global, ponieważ ciąg nie jest globalny, po prostu żyje w zewnętrznym zasięgu. Pomysły?

+0

Może być powiązany: http://stackoverflow.com/q/146359/212218 –

Odpowiedz

35

w Pythonie 3.x, można użyć nonlocal kluczowe:

def outer(): 
    string = "" 
    def inner(): 
     nonlocal string 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

w Pythonie 2.x, można użyć listy z jednego elementu i zastąpić ten jeden element:

def outer(): 
    string = [""] 
    def inner(): 
     string[0] = "String was changed by a nested function!" 
    inner() 
    return string[0] 
+0

+1 Jeśli chodzi o dlaczego ta ostatnia działa: Przypisanie elementu (wraz z przypisaniem atrybutu) nie jest przypisaniem zmiennym - 'x [...] = ...' po prostu dostaje 'x' z dowolnego zakresu, w jakim się znajduje, i wywołuje jego metodę' __setitem__' . Równie ważną, ale początkowo bardziej szczegółową opcją jest stworzenie obiektu (klasy zdefiniowanej przez użytkownika, ponieważ 'object()' nie pozwoli na dodanie atrybutów), którego jedynym celem jest posiadanie atrybutu. – delnan

+2

Możesz stworzyć taki "obiekt przestrzeni nazw" używając 'x = type (" ",(), {})()'. :) –

+0

Tak więc, 'nonlokalny' oznacza:" szukaj tej zmiennej w zewnętrznym zasięgu "- gdyby zmienna nie została znaleziona w zakresie zewnętrznym(), czy Python dalej szukałby w otaczających zakresach? Czy Python ostatecznie spróbuje znaleźć zmienną w zasięgu globalnym? – Ord

2

aby dodać do Sven's answer:

w Pythonie 2.x, można odczytać tylko zewnętrzne zmienne, zakres od karczmy er zakres. Przypisanie spowoduje tylko utworzenie nowej zmiennej lokalnej (tj. Wewnętrznego zakresu), która ukrywa zewnętrzny zakres.

Jeśli chcesz czytać i modyfikować, można użyć dict trzymać zmienne w zakresie zewnętrznym, a następnie uzyskać do nich dostęp za pośrednictwem dict w zakresie wewnętrznym, a także utrzymywanie kodu dość czysty i czytelny w obecności wielokrotności zakres zewnętrzna vars:

def outer(): 
    # hold some text, plus the number of spaces in the text 
    vars = {'text': 'Some text.', 'num_spaces': 1} 
    def inner(): 
     # add some more text 
     more_text = ' Then some more text.' 
     vars['text'] += more_text 
     # keep track of the number of spaces 
     vars['num_spaces'] += more_text.count(' ') 
    inner() 
    return vars['text'], vars['num_spaces'] 

Wydajność:

>>> outer() 
('Some text. Then some more text.', 5) 
19

Można również obejść ten problem za pomocą funkcji atrybuty:

def outer(): 
    def inner(): 
     inner.string = "String was changed by a nested function!" 
    inner.string = "" 
    inner() 
    return inner.string 

Wyjaśnienie: ten działa zarówno 2.x Python i 3.x.

+3

To naprawdę świetny pomysł. – Alice

3

To zdarza mi się zbyt często, kiedy pisałem funkcję i nagle uświadamiam sobie, że dobrym pomysłem może być posiadanie mniejszej funkcji pomocnika, ale niezbyt użytecznej nigdzie indziej. co naturalnie sprawia, że ​​chcę go zdefiniować wewnątrz jako funkcję zagnieżdżoną.

, ale miałem doświadczenie z anonimowym obiektem JAVA (tj. Zdefiniować działający), a reguła polegała na tym, że anonimowy obiekt tworzy kopię swojego zewnętrznego środowiska, w tym przypadku zmienne zewnętrznego zakresu. Zatem, jeśli zmienna zewnętrzna jest niezmienna (int,), nie mogą być modyfikowane przez anonimowy obiekt, ponieważ są kopiowane przez wartość, natomiast jeśli jest zmienna (collection, objects), można je zmienić ... ponieważ są kopiowane przez "wskaźnik" (ich adres w pamięci)

jeśli znasz programowanie, pomyśl o nim jako o wartości dodanej i podaj jako odniesienie.

w python, to bardzo to samo.x=123 jest zadanie, dają zmiennej xa nowego znaczenia (nie modyfikować stare X), list[i]/dict[key] są operacje dostępu do obiektów, naprawdę modyfikować rzeczy

stwierdzić, trzeba zmienny obiekt w celu ... zmodyfikuj (mimo że możesz uzyskać dostęp do krotki za pomocą [], nie możesz jej użyć tutaj, ponieważ nie można jej zmienić)