Próbuję zaimplementować zamknięcie w Pythonie 2.6 i muszę uzyskać dostęp do zmiennej nielokalnej, ale wygląda na to, że to słowo kluczowe nie jest dostępne w Pythonie 2.x. Jak uzyskać dostęp do zmiennych nielokalnych w domknięciach w tych wersjach Pythona?
117
def inner(): print d; d = {'y': 1}
. Tutajprint d
odczytuje zewnętrzną,d
tworząc w ten sposób zmienną nielokalnąd
w zakresie wewnętrznym.X = 1
po prostu wiąże nazwęX
z konkretnym obiektem (int
z wartością1
).X = 1; Y = X
wiąże dwie nazwy z tym samym dokładnym obiektem. W każdym razie niektóre obiekty są zmienne i możesz zmienić ich wartości.Poniższe rozwiązanie jest inspirowane odpowiedzią Eliasa Zamarii , ale w przeciwieństwie do tej odpowiedzi poprawnie obsługuje wielokrotne wywołania funkcji zewnętrznej. „Zmienna”
inner.y
jest lokalna dla bieżącego wywołaniaouter
. Tyle tylko, że nie jest to zmienna, ponieważ jest to zabronione, ale atrybut obiektu (obiekt jestinner
samą funkcją ). Jest to bardzo brzydkie (zauważ, że atrybut można utworzyć dopiero poinner
zdefiniowaniu funkcji), ale wydaje się skuteczne.źródło
inc()
idec()
zwrócone z zewnątrz, które zwiększają i zmniejszają wspólny licznik. Następnie musisz zdecydować, do której funkcji dołączyć bieżącą wartość licznika i odwołać się do tej funkcji z innej funkcji. Co wygląda nieco dziwnie i asymetrycznie. Np. Wdec()
linii jakinc.value -= 1
.Zamiast słownika jest mniej bałaganu w klasie nielokalnej . Modyfikowanie przykładu @ ChrisB :
Następnie
Każde wywołanie external () tworzy nową i odrębną klasę o nazwie context (a nie tylko nową instancję). Dzięki temu unika się ostrożności @ Nathaniel na temat współdzielonego kontekstu.
źródło
__slots__ = ()
i tworząc obiekt zamiast używać klasy, na przykładcontext.z = 3
podniósłby plikAttributeError
. Jest to możliwe dla wszystkich klas, chyba że dziedziczą one po klasie nie definiującej slotów.Myślę, że kluczem jest tutaj to, co rozumiesz przez „dostęp”. Nie powinno być problemu z odczytem zmiennej spoza zakresu zamknięcia, np.
powinien działać zgodnie z oczekiwaniami (drukowanie 3). Jednak przesłanianie wartości x nie działa, np.
będzie nadal drukować 3. Z mojego zrozumienia PEP-3104 właśnie to ma obejmować słowo kluczowe nonlocal. Jak wspomniano w PEP, możesz użyć klasy, aby osiągnąć to samo (trochę bałagan):
źródło
def ns(): pass
po której następujens.x = 3
. Nie jest ładny, ale dla mojego oka jest nieco mniej brzydki.class Namespace: x = 3
?ns
jest obiektem globalnym, dlatego możesz odwołać sięns.x
na poziomie modułu wprint
instrukcji na samym końcu .Istnieje inny sposób implementacji zmiennych nielokalnych w Pythonie 2, na wypadek gdyby którakolwiek z odpowiedzi tutaj była niepożądana z jakiegokolwiek powodu:
Używanie nazwy funkcji w instrukcji przypisania zmiennej jest zbędne, ale wydaje mi się to prostsze i czystsze niż umieszczenie zmiennej w słowniku. Wartość jest zapamiętywana z jednego wezwania do drugiego, tak jak w odpowiedzi Chrisa B.
źródło
f = outer()
a później to zrobiszg = outer()
,f
licznik zostanie zresetowany. To dlatego, że oboje mają wspólny pojedynczaouter.y
zmienna, a nie każdy posiada własną niezależną jeden. Chociaż ten kod wygląda bardziej estetycznie niż odpowiedź Chrisa B, jego sposób wydaje się być jedynym sposobem na emulację leksykalnego zakresu, jeśli chcesz wywołaćouter
więcej niż jeden raz.outer.y
nie obejmuje niczego lokalnego dla wywołania funkcji (instancji)outer()
, ale przypisuje atrybut obiektu funkcji, który jest powiązany z nazwąouter
w otaczającym ją zakresie. A zatem można by równie dobrze wykorzystane, na piśmieouter.y
, żadnego innego imienia, a nieouter
, pod warunkiem, że wiadomo, że jest związany w tym zakresie. Czy to jest poprawne?outer.y
użyć nazwyinner.y
(ponieważinner
jest ona związana wewnątrz wywołaniaouter()
, co jest dokładnie takim zakresem, jaki chcemy), ale umieszczając inicjalizacjainner.y = 0
po zdefiniowaniu inner (bo obiekt musi istnieć, gdy tworzony jest atrybut), ale oczywiście przedreturn inner
?Oto coś zainspirowanego sugestią, którą Alois Mahdal poczynił w komentarzu dotyczącym innej odpowiedzi :
Aktualizacja
Kiedy ostatnio spojrzałem na to wstecz, uderzyło mnie to, jak wygląda dekorator - kiedy dotarło do mnie, że wdrożenie go w taki sposób, aby było bardziej ogólne i użyteczne (chociaż prawdopodobnie w pewnym stopniu obniża to jego czytelność).
Zauważ, że obie wersje działają zarówno w Pythonie 2, jak i 3.
źródło
W regułach określania zakresu Pythona jest brodawka - przypisanie powoduje, że zmienna jest lokalna dla jej bezpośrednio obejmującego zakres funkcji. W przypadku zmiennej globalnej można to rozwiązać za pomocą
global
słowa kluczowego.Rozwiązaniem jest wprowadzenie obiektu, który jest współdzielony między dwoma zakresami, który zawiera zmienne modyfikowalne, ale do niego odwołuje się zmienna, która nie jest przypisana.
Alternatywą jest hakowanie niektórych zakresów:
Być może będziesz w stanie wymyślić jakąś sztuczkę, aby uzyskać nazwę parametru
outer
, a następnie przekazać ją jako nazwę zmiennej, ale bez polegania na nazwie,outer
której chciałbyś użyć kombinatora Y.źródło
nonlocal
.locals()
tworzy słownikouter()
s locals w tym czasieinner()
jest zdefiniowany, ale zmiana tego słownika nie zmieniav
inouter()
. To już nie zadziała, gdy masz więcej funkcji wewnętrznych, które chcą udostępniać zmienną zamkniętą. Powiedz a,inc()
adec()
zwiększaj i zmniejszaj wspólny licznik.nonlocal
to funkcja Pythona 3.nonlocal
w Pythonie 2 . Twoje pomysły nie obejmują przypadku ogólnego, ale tylko ten z jedną funkcją wewnętrzną. Spójrz na tę istotę jako przykład. Obie funkcje wewnętrzne mają swój własny kontener. Potrzebujesz zmiennego obiektu w zakresie funkcji zewnętrznej, jak sugerowały już inne odpowiedzi.nonlocal
słowa kluczowego wprowadzonego w Pythonie 3.Inny sposób, aby to zrobić (chociaż jest zbyt szczegółowy):
źródło
Rozszerzając eleganckie rozwiązanie Martineau powyżej o praktyczny i nieco mniej elegancki przypadek użycia otrzymuję:
źródło
Użyj zmiennej globalnej
Osobiście nie lubię zmiennych globalnych. Ale moja propozycja jest oparta na odpowiedzi https://stackoverflow.com/a/19877437/1083704
gdzie użytkownik musi zadeklarować zmienną globalną
ranks
, za każdym razem, gdy musisz wywołać metodęreport
. Moje ulepszenie eliminuje potrzebę inicjowania zmiennych funkcji od użytkownika.źródło
inner
, ale nie możesz jej przypisywać, ale możesz modyfikować jej klucze i wartości. Pozwala to uniknąć używania zmiennych globalnych.