Instrukcja nielokalna w języku Python

341

Do czego służy nonlocalinstrukcja Python (w Python 3.0 i nowszych)?

Na oficjalnej stronie Python help("nonlocal")nie ma dokumentacji i też nie działa.

ooboo
źródło
18
Oto oficjalna dokumentacja strony Python dla nielokalnych: docs.python.org/3/reference/… (ta dokumentacja jest dostępna od Python 3.0, więc twierdzenie OP, że nie ma oficjalnej dokumentacji, było po prostu błędne)
wkschwartz
3
"There is no documentation for nonlocal".W rzeczywistości możesz zrobić help(keyword_in_string)dokumentację w Pythonie 3 i nowszych
ytpillai
10
Aby być uczciwym, oficjalni doktorzy w pewien sposób ssają ten temat. Przykład wybranej odpowiedzi wyjaśnia wszystko, czyniąc z tego cenne pytanie.
Szalony fizyk
W oficjalnym samouczku w języku Python znajduje się dobre objaśnienie pojęcia zakresów i przestrzeni nazw z dobrym przykładem .
jammon

Odpowiedzi:

473

Porównaj to bez użycia nonlocal:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0

Aby to zrobić, używając nonlocal, gdzie inner()„s xjest teraz także outer()” s x:

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0

Gdybyśmy mieli użyć global, wiązałoby się xto z poprawnie „globalną” wartością:

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2
Zaraz
źródło
32
Czym to się różni od globalnego x?
ooboo,
52
Jest bardzo podobny - ale zauważ, że zewnętrzny x nie jest globalny w tym przykładzie, ale jest zamiast tego zdefiniowany w funkcji zewnętrznej.
Anon
3
@Dustin - W rzeczywistości, gdybyś miał klasę A z atrybutem x i zdefiniowaną w niej podklasą B, odnosiłbyś się do x z B jako Ax
Anon
2
Kod łatwo się mocno wcina podczas definiowania funkcji wewnętrznych i ostatecznie narusza 79-znakową rekomendację PEP8. Jest jakiś sposób na obejście tego problemu? Czy funkcję wewnętrzną można w jakiś sposób umieścić poza funkcją zewnętrzną? Wiem, że pytanie brzmi głupio, ale jestem szczery.
tommy.carstensen
3
@ tommy.carstensen możesz przekazać tę funkcję jako argument, który jest pięknem funkcji wyższego rzędu. Również w programowaniu funkcjonalnym nazywa się to kompozycją, python nie jest czystym językiem FP, ale z pewnością możesz bawić się funkcjami (generatory, funkcje wyższego rzędu to tylko niektóre przykłady)
superuseroi
90

Krótko mówiąc, pozwala przypisywać wartości do zmiennej w zewnętrznym (ale nie globalnym) zakresie. Zobacz PEP 3104, aby uzyskać wszystkie krwawe szczegóły.

Arkady
źródło
41

Wyszukiwarka Google dla „python nonlocal” ujawniła propozycję, PEP 3104 , która w pełni opisuje składnię i uzasadnienie instrukcji. w skrócie, działa dokładnie tak samo jak globalinstrukcja, z tym wyjątkiem, że odnosi się do zmiennych, które nie są globalne ani lokalne dla funkcji.

Oto krótki przykład tego, co możesz z tym zrobić. Licznik generatora można przepisać, aby go użyć, aby wyglądał bardziej jak idiomy języków z zamknięciami.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

Oczywiście możesz napisać to jako generator, na przykład:

def counter_generator():
    count = 0
    while True:
        count += 1
        yield count

Ale chociaż jest to całkowicie idiomatyczny python, wydaje się, że pierwsza wersja byłaby nieco bardziej oczywista dla początkujących. Prawidłowe używanie generatorów, poprzez wywołanie zwróconej funkcji, jest częstym punktem zamieszania. Pierwsza wersja wyraźnie zwraca funkcję.

SingleNegationElimination
źródło
1
Byłem pewien, że to właśnie robi słowo kluczowe „globalny” - działa w wyższych środowiskach, dopóki nie osiągnie zmiennej o tej nazwie. zmienna x może być zadeklarowana na poziomie modułu, wewnątrz klasy, a następnie oddzielnie w funkcji w tej klasie, a następnie w wewnętrznej funkcji tej funkcji - skąd ona wie, do którego x się odwoływać?
ooboo,
7
globalną rzeczą jest to, że działa tylko dla zmiennych globalnych. nie widzi zmiennych w zamkniętym, nieglobalnym zakresie.
SingleNegationElimination
Próbowałem make_counter - jednak nie zwraca generatora, ale funkcję. czy istnieje sposób na zwrócenie generatora, aby później móc go iterować?
Dejell,
@Dejel: ten przykład ilustruje nonlocalinstrukcję w Pythonie; Jeśli chcesz sekwencję liczb naturalnych, idiom python jest w rzeczywistościitertools.count()
SingleNegationElimination
Chciałbym zademonstrować możliwość zwrotu generatora jak z wydajnością - wydajność faktycznie zwraca generator. Moim pomysłem nie jest wykorzystanie plonu, a zamiast tego być może zastosowanie nielokalne lub inne rozwiązanie
Dejell
15

@ooboo:

Zajmuje jeden „najbliższy” do punktu odniesienia w kodzie źródłowym. Nazywa się to „leksykalnym ustalaniem zakresu” i jest standardem od ponad 40 lat.

Członkowie klasy Pythona tak naprawdę znajdują się w słowniku o nazwie __dict__ i nigdy nie zostanie osiągnięty przez zakres leksykalny.

Jeśli nie podasz, nonlocalale zrobisz x = 7, utworzy nową lokalną zmienną „x”. Jeśli podasz nonlocal, znajdzie „najbliższe” „x” i przypisze do tego. Jeśli podasznonlocal i nie ma „x”, wyświetli się komunikat o błędzie.

Słowo kluczowe globalzawsze wydawało mi się dziwne, ponieważ z radością zignoruje wszystkie pozostałe „x”, z wyjątkiem najbardziej zewnętrznego. Dziwne.

Danny Milosavljevic
źródło
14

help („nielokalne”) nonlocalInstrukcja


    nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

The nonlocalZestawienie powoduje, że wymienione identyfikatory odnoszą się do poprzednio związany zmiennych w najbliższym zakresu okalającego. Jest to ważne, ponieważ domyślnym zachowaniem wiązania jest przeszukiwanie najpierw lokalnej przestrzeni nazw. Instrukcja pozwala enkapsulowanemu kodowi na ponowne powiązanie zmiennych poza zakresem lokalnym oprócz zakresu globalnego (modułu).

Nazwy wymienione w nonlocalinstrukcji, w przeciwieństwie do nazw wymienionych wglobal instrukcji, muszą odnosić się do wcześniej istniejących powiązań w zakresie obejmującym (zakresu, w którym należy utworzyć nowe powiązanie, nie można jednoznacznie określić).

Nazwy wymienione w nonlocalinstrukcji nie mogą kolidować z wcześniej istniejącymi powiązaniami w zakresie lokalnym.

Zobacz też:

PEP 3104 - Dostęp do nazw w zewnętrznych zakresach
Specyfikacja nonlocalinstrukcji.

Powiązane tematy pomocy: globalna, NAMESPACES

Źródło: Python Language Reference

Yossi Truzman
źródło
11
Naucz się czegoś nowego każdego dnia. Nie miałem pojęcia, że ​​możesz użyć help()słów kluczowych (a teraz jestem podekscytowany: help()bez argumentów wchodzi w interakcję ).
Erik Youngren
6

Cytat z Python 3 Reference :

Instrukcja nielokalna powoduje, że wymienione identyfikatory odnoszą się do poprzednio powiązanych zmiennych w najbliższym obejmującym zakresie, z wyłączeniem globali.

Jak wspomniano w odnośniku, w przypadku kilku zagnieżdżonych funkcji modyfikowana jest tylko zmienna w najbliższej otaczającej funkcji:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

„Najbliższa” zmienna może znajdować się kilka poziomów dalej:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

Ale nie może to być zmienna globalna:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found
Jeyekomon
źródło
3
a = 0    #1. global variable with respect to every function in program

def f():
    a = 0          #2. nonlocal with respect to function g
    def g():
        nonlocal a
        a=a+1
        print("The value of 'a' using nonlocal is ", a)
    def h():
        global a               #3. using global variable
        a=a+5
        print("The value of a using global is ", a)
    def i():
        a = 0              #4. variable separated from all others
        print("The value of 'a' inside a function is ", a)

    g()
    h()
    i()
print("The value of 'a' global before any function", a)
f()
print("The value of 'a' global after using function f ", a)
gxyd
źródło
2

Moje osobiste rozumienie instrukcji „nielokalnej” (i przepraszam, ponieważ jestem nowy w Pythonie i programowaniu w ogóle) jest takie, że „nielokalna” jest sposobem na użycie funkcji globalnej w ramach iterowanych funkcji, a nie samej treści samego kodu . Globalna instrukcja między funkcjami, jeśli chcesz.

Yossi Truzman
źródło
0

z „nielokalnymi” funkcjami wewnętrznymi (tj. zagnieżdżonymi funkcjami wewnętrznymi) można uzyskać uprawnienia do odczytu i zapisu dla tej konkretnej zmiennej zewnętrznej funkcji rodzica . I nielokalne mogą być używane tylko wewnątrz funkcji wewnętrznych, np .:

a = 10
def Outer(msg):
    a = 20
    b = 30
    def Inner():
        c = 50
        d = 60
        print("MU LCL =",locals())
        nonlocal a
        a = 100
        ans = a+c
        print("Hello from Inner",ans)       
        print("value of a Inner : ",a)
    Inner()
    print("value of a Outer : ",a)

res = Outer("Hello World")
print(res)
print("value of a Global : ",a)
NIPHIN
źródło