Istruzione non locale Python


341

Cosa fa l' nonlocalistruzione Python (in Python 3.0 e successive)?

Non c'è documentazione sul sito Web ufficiale di Python e help("nonlocal")non funziona neanche.



18
Ecco la documentazione ufficiale del sito Web Python per non locali: docs.python.org/3/reference/… (questa documentazione è disponibile da Python 3.0, quindi l'affermazione dell'OP che non esiste alcuna documentazione ufficiale era semplicemente sbagliata)
wkschwartz,

3
"There is no documentation for nonlocal".In realtà, puoi fare help(keyword_in_string)per le documentazioni in Python 3 e versioni successive
ytpillai,

10
Ad essere onesti, i documenti ufficiali fanno schifo sull'argomento. L'esempio della risposta selezionata rende le cose molto chiare, rendendola una domanda preziosa.
Fisico pazzo,

Nel tutorial ufficiale di Python c'è una buona spiegazione del concetto di ambiti e spazi dei nomi con un buon esempio .
Jammon,

Risposte:


473

Confronta questo, senza usare 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

Per questo, utilizzando nonlocal, dove inner()'s xè ora anche 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

Se dovessimo utilizzarlo global, si legherebbe xal valore "globale" correttamente:

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

32
In che modo è diverso dalla x globale?
ooboo,

52
È molto simile, ma nota che la x esterna non è globale nell'esempio ma è invece definita nella funzione esterna.
Anon,

3
@Dustin - In realtà, se avessi classe A con un attributo x e una sottoclasse B definite in essa, faresti riferimento a x all'interno di B come Ax
Anon,

2
Il codice diventa facilmente rientrato quando si definiscono le funzioni interne e finisce per violare la raccomandazione PEP8 di 79 caratteri. Un modo per aggirare questo problema? Una funzione interna può in qualche modo essere collocata al di fuori della funzione esterna? So che la domanda sembra stupida, ma sono sincero.
tommy.carstensen,

3
@ tommy.carstensen potresti passare la funzione come argomento che è la bellezza delle funzioni di ordine superiore. Anche nella programmazione funzionale questo si chiama composizione, python non è un puro linguaggio FP ma puoi certamente giocare con una caratteristica (generatori, funzioni di ordine superiore sono alcuni esempi)
superuseroi

90

In breve, ti consente di assegnare valori a una variabile in un ambito esterno (ma non globale). Vedi PEP 3104 per tutti i dettagli cruenti.


41

Una ricerca su Google per "python nonlocal" ha rivelato la proposta, PEP 3104 , che descrive completamente la sintassi e il ragionamento alla base dell'istruzione. in breve, funziona esattamente allo stesso modo globaldell'istruzione, tranne per il fatto che è usato per riferirsi a variabili che non sono né globali né locali alla funzione.

Ecco un breve esempio di cosa puoi fare con questo. Il contro generatore può essere riscritto per usarlo in modo che assomigli di più ai modi di dire delle lingue con le chiusure.

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

Ovviamente, potresti scrivere questo come generatore, come:

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

Ma mentre questo è un pitone perfettamente idiomatico, sembra che la prima versione sia un po 'più ovvia per i principianti. L'uso corretto dei generatori, chiamando la funzione restituita, è un punto comune di confusione. La prima versione restituisce esplicitamente una funzione.


1
Ero sicuro che è ciò che fa la parola chiave "globale": crea ambienti più alti fino a quando non raggiunge una variabile con quel nome. una variabile x può essere dichiarata a livello di modulo, all'interno di una classe, quindi separatamente in una funzione all'interno di questa classe e quindi in una funzione interna di quella funzione - come fa a sapere a quale x fare riferimento?
ooboo,

7
la cosa su global è che funziona solo con variabili globali. non può vedere le variabili in un ambito chiuso e non globale.
SingleNegationElimination

Ho provato make_counter - tuttavia non restituisce un generatore ma una funzione. c'è un modo per restituire un generatore così più tardi potrei iterare su di esso?
Dejell,

@Dejel: questo esempio ha lo scopo di illustrare l' nonlocalistruzione in Python; Se vuoi una sequenza di numeri naturali, il linguaggio dei pitoni è in realtàitertools.count()
SingleNegationElimination

Vorrei dimostrare la capacità di restituire un generatore come con la resa - la resa in realtà restituisce un generatore. La mia idea non è quella di utilizzare la resa e invece forse usare una soluzione non locale o un'altra soluzione
Dejell

15

@ooboo:

Prende quello "più vicino" al punto di riferimento nel codice sorgente. Questo si chiama "scopical lessicale" ed è standard da oltre 40 anni.

I membri della classe di Python si trovano davvero in un dizionario chiamato __dict__e non saranno mai raggiunti dallo scoping lessicale.

Se non si specifica nonlocalma si x = 7, creerà una nuova variabile locale "x". Se lo specifichi nonlocal, troverà il "più vicino" "x" e assegnerà a quello. Se si specifica nonlocale non è presente una "x", verrà visualizzato un messaggio di errore.

La parola chiave mi globalè sempre sembrata strana poiché ignorerà felicemente tutte le altre "x" tranne quella più esterna. Strano.


14

help ('nonlocal') L' nonlocalistruzione


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

L' nonlocalistruzione fa sì che gli identificatori elencati facciano riferimento a variabili precedentemente associate nell'ambito più vicino. Ciò è importante perché il comportamento predefinito per l'associazione è la ricerca prima nello spazio dei nomi locale. L'istruzione consente al codice incapsulato di ricollegare le variabili al di fuori dell'ambito locale oltre all'ambito globale (modulo).

I nomi elencati in nonlocalun'istruzione, a differenza di quelli elencati in globalun'istruzione, devono fare riferimento a associazioni preesistenti in un ambito di inclusione (l'ambito in cui creare una nuova associazione non può essere determinato in modo univoco).

I nomi elencati in nonlocalun'istruzione non devono scontrarsi con associazioni preesistenti nell'ambito locale.

Guarda anche:

PEP 3104 - Accesso ai nomi negli ambiti esterni
La specifica nonlocaldell'istruzione.

Argomenti della guida correlati: globale, NAMESPACES

Fonte: Python Language Reference


11
Impara qualcosa di nuovo ogni giorno. Non avevo idea che potessi usare le help()parole chiave (e ora la mia mente è saltata: help()senza argomenti diventa interattivo ).
Erik Youngren,

6

Citazione dal riferimento di Python 3 :

L'istruzione non locale fa sì che gli identificatori elencati facciano riferimento a variabili precedentemente associate nell'ambito compreso più vicino, esclusi i globi.

Come detto nel riferimento, in caso di più funzioni nidificate viene modificata solo la variabile nella funzione di chiusura più vicina:

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

La variabile "più vicina" può essere a diversi livelli di distanza:

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

Ma non può essere una variabile globale:

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

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)

2

La mia personale comprensione dell'affermazione "nonlocale" (e mi scusi perché sono nuovo di Python e della programmazione in generale) è che il "nonlocale" è un modo per utilizzare la funzionalità globale all'interno delle funzioni iterate piuttosto che il corpo del codice stesso . Una dichiarazione globale tra le funzioni, se vuoi.


0

con funzioni interne "non locali" (ovvero funzioni interne annidate) è possibile ottenere l'autorizzazione di lettura e " scrittura " per quella specifica variabile della funzione genitore esterna . E il nonlocale può essere utilizzato solo all'interno di funzioni interne, ad esempio:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.