Declaración no local de Python

340

¿Qué hace la nonlocaldeclaración de Python (en Python 3.0 y posterior)?

No hay documentación en el sitio web oficial de Python y help("nonlocal")tampoco funciona.

ooboo
fuente
44
Eche un vistazo a esta pregunta: stackoverflow.com/questions/1414304/local-functions-in-python
Matt Joiner
18
Aquí está la documentación oficial del sitio web de Python para no local: docs.python.org/3/reference/… (esta documentación ha estado disponible desde Python 3.0, por lo que la afirmación del OP de que no hay documentación oficial fue simplemente incorrecta)
wkschwartz
3
"There is no documentation for nonlocal".En realidad, puedes hacerlo help(keyword_in_string)para documentaciones en Python 3 y superior
ytpillai
10
Para ser justos, los documentos oficiales apestan sobre el tema. El ejemplo de la respuesta seleccionada deja las cosas muy claras, haciendo de esta una pregunta valiosa.
Físico loco
En el tutorial oficial de Python hay una buena explicación del concepto de ámbitos y espacios de nombres con un buen ejemplo .
jammon

Respuestas:

471

Compare esto, sin usar 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

Para esto, usando nonlocal, donde inner()'s xes ahora también 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

Si tuviéramos que usarlo global, se uniría xal valor "global" apropiado:

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
Luego
fuente
32
¿Cómo es eso diferente de x global?
ooboo
52
Es muy similar, pero tenga en cuenta que la x externa no es global en el ejemplo, sino que se define en la función externa.
Anon
3
@Dustin - En realidad, si tuvieras la clase A con un atributo xy una subclase B definida en él, te referirías a x desde B como Ax
Anon
2
El código se sangra mucho al definir funciones internas y termina violando la recomendación PEP8 de 79 caracteres. ¿Alguna forma de solucionar este problema? ¿Se puede colocar una función interna fuera de la función externa? Sé que la pregunta suena estúpida, pero soy sincero.
tommy.carstensen
3
@ tommy.carstensen podría pasar la función como un argumento que es la belleza de las funciones de orden superior. También en la programación funcional esto se llama composición, python no es un lenguaje FP puro, pero ciertamente puedes jugar con características (generadores, funciones de orden superior son algunos ejemplos)
superuseroi
90

En resumen, le permite asignar valores a una variable en un ámbito externo (pero no global). Ver PEP 3104 para todos los detalles sangrientos.

Arkady
fuente
41

Una búsqueda en Google de "python nonlocal" arrojó la Propuesta, PEP 3104 , que describe completamente la sintaxis y el razonamiento detrás de la declaración. en resumen, funciona exactamente de la misma manera que la globaldeclaración, excepto que se usa para referirse a variables que no son globales ni locales para la función.

Aquí hay un breve ejemplo de lo que puede hacer con esto. El generador de contadores se puede reescribir para usar esto de modo que se parezca más a los modismos de idiomas con cierres.

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

Obviamente, podría escribir esto como un generador, como:

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

Pero si bien esta es una pitón perfectamente idiomática, parece que la primera versión sería un poco más obvia para los principiantes. El uso adecuado de generadores, llamando a la función devuelta, es un punto común de confusión. La primera versión devuelve explícitamente una función.

SingleNegationElimination
fuente
1
Estaba seguro de que eso es lo que hace la palabra clave 'global': funciona en entornos superiores hasta que alcanza una variable con ese nombre. una variable x podría declararse a nivel de módulo, dentro de una clase, luego por separado en una función dentro de esta clase y luego en una función interna de esa función: ¿cómo sabe a qué x referirse?
ooboo
77
Lo global es que solo funciona para variables globales. no puede ver variables en un ámbito no global que lo encierra.
SingleNegationElimination
Probé el make_counter, sin embargo, no devuelve un generador sino una función. ¿Hay alguna manera de devolver un generador para que luego pueda iterar sobre él?
Dejell
@Dejel: este ejemplo pretende ilustrar la nonlocaldeclaración en Python; Si desea una secuencia de números naturales, el idioma de Python es en realidaditertools.count()
SingleNegationElimination
Me gustaría demostrar la capacidad de devolver un generador como con rendimiento: el rendimiento en realidad devuelve un generador. Mi idea es no usar el rendimiento y, en su lugar, usar una solución no local u otra
Dejell
15

@ooboo:

Lleva el "más cercano" al punto de referencia en el código fuente. Esto se llama "alcance léxico" y es estándar por más de 40 años.

Los miembros de la clase de Python están realmente en un diccionario llamado __dict__y nunca serán alcanzados por el alcance léxico.

Si no especifica nonlocalpero lo hace x = 7, creará una nueva variable local "x". Si lo especifica nonlocal, encontrará la "x" más cercana y se la asignará. Si especifica nonlocaly no hay "x", le dará un mensaje de error.

La palabra clave globalsiempre me ha parecido extraña ya que felizmente ignorará todas las otras "x", excepto la más externa. Extraño.

Danny Milosavljevic
fuente
14

ayuda ('no local') La nonlocaldeclaración


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

La nonlocaldeclaración hace que los identificadores enumerados hagan referencia a variables previamente vinculadas en el ámbito de cierre más cercano. Esto es importante porque el comportamiento predeterminado para el enlace es buscar primero el espacio de nombres local. La declaración permite que el código encapsulado vuelva a enlazar variables fuera del alcance local además del alcance global (módulo).

Los nombres enumerados en una nonlocaldeclaración, a diferencia de los enumerados en una globaldeclaración, deben referirse a enlaces preexistentes en un ámbito adjunto (el alcance en el que se debe crear un nuevo enlace no puede determinarse sin ambigüedades).

Los nombres enumerados en una nonlocaldeclaración no deben colisionar con enlaces preexistentes en el ámbito local.

Ver también:

PEP 3104 - Acceso a nombres en ámbitos externos
La especificación de la nonlocaldeclaración.

Temas de ayuda relacionados: global, NAMESPACES

Fuente: Referencia del lenguaje Python

Yossi Truzman
fuente
11
Aprenda algo nuevo cada día. No tenía idea de que podría usar help()palabras clave (y ahora mi mente está asombrada: help()sin argumentos se vuelve interactivo ).
Erik Youngren
6

Cita de la referencia de Python 3 :

La declaración no local hace que los identificadores enumerados hagan referencia a variables previamente vinculadas en el ámbito de cierre más cercano, excluyendo los globales.

Como se dijo en la referencia, en el caso de varias funciones anidadas, solo se modifica la variable en la función de cierre más cercana:

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 variable "más cercana" puede estar a varios niveles:

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

Pero no puede ser una variable global:

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
fuente
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
fuente
2

Mi comprensión personal de la declaración "no local" (y discúlpeme ya que soy nuevo en Python y la Programación en general) es que "no local" es una forma de usar la funcionalidad Global dentro de funciones iteradas en lugar del cuerpo del código en sí . Una declaración global entre funciones, por así decirlo.

Yossi Truzman
fuente
0

con funciones internas 'no locales' (es decir, funciones internas anidadas) puede obtener permiso de lectura y ' escritura ' para esa variable específica de la función primaria externa . Y no local solo puede usarse dentro de funciones internas, por ejemplo:

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
fuente