Breve descripción de las reglas de alcance?

472

¿Cuáles son exactamente las reglas de alcance de Python?

Si tengo un código:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Donde se xencuentra Algunas opciones posibles incluyen la siguiente lista:

  1. En el archivo fuente adjunto
  2. En el espacio de nombres de clase
  3. En la definición de la función
  4. En la variable de índice for loop
  5. Dentro del bucle for

También existe el contexto durante la ejecución, cuando la función spamse pasa a otro lugar. ¿Y quizás las funciones lambda pasan un poco diferente?

Debe haber una referencia o algoritmo simple en alguna parte. Es un mundo confuso para los programadores intermedios de Python.

Charles Merriam
fuente
2
Las reglas de alcance se describen de manera bastante concisa, pero también completamente, en la documentación de Python: docs.python.org/3/reference/… .
jefe2000

Respuestas:

420

En realidad, una regla concisa para la resolución de Python Scope, de Learning Python, 3rd. Ed. . (Estas reglas son específicas de nombres de variables, no de atributos. Si hace referencia a ellas sin un punto, se aplican estas reglas).

Regla LEGB

  • L ocal: nombres asignados de cualquier forma dentro de una función ( defo lambda), y no declarados globales en esa función

  • E nclosing-función - Nombres asignados en el ámbito local de cualquier y todas las funciones estáticamente de cerramiento ( defo lambda), del interior al exterior

  • G lobal (módulo): nombres asignados en el nivel superior de un archivo de módulo o ejecutando una globaldeclaración en un defarchivo

  • B uilt-in (Python) - Los nombres preasignados en el módulo incorporado nombres: open, range, SyntaxError, etc.

Entonces, en el caso de

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

El forbucle no tiene su propio espacio de nombres. En orden LEGB, los ámbitos serían

  • L: en local def spam(en code3, code4y code5)
  • E: cualquier función de cierre (si todo el ejemplo estuviera en otro def)
  • G: ¿Hubo alguna xdeclarada globalmente en el módulo (in code1)?
  • B: Cualquier incorporado xen Python.

xnunca se encontrará en code2(incluso en los casos en que podría esperar que lo haga, vea la respuesta de Antti o aquí ).

Rizwan Kassim
fuente
45
Como advertencia para el acceso global: la lectura de una variable global puede ocurrir sin una declaración explícita, pero escribir en ella sin declarar global (var_name) creará una nueva instancia local.
Peter Gibson
12
En realidad @Peter, global(var_name)es sintácticamente incorrecto. La sintaxis correcta sería global var_namesin paréntesis. Sin embargo, tienes un punto válido.
Martineau
Si es así, ¿por qué la variable "y" de foo no es visible para la "barra" a continuación: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer
3
@ Jonathan: Debido a que cada uno yestá siendo escrito y no hay global ydeclaraciones, vea el comentario de @ Peter.
Martineau
@LakshmanPrasad Cae en "E", pero tiene un comportamiento especial que vale la pena mencionar: es una variable de clase, por lo que es un "global" entre sus objetos. Asignando para ello tendrá lugar a imprevistos y difícil de depurar problemas si usted no sabe lo que está haciendo.
Ctrl-C
157

Esencialmente, lo único en Python que introduce un nuevo alcance es una definición de función. Las clases son un poco un caso especial en el sentido de que cualquier cosa definida directamente en el cuerpo se coloca en el espacio de nombres de la clase, pero no son accesibles directamente desde los métodos (o clases anidadas) que contienen.

En su ejemplo, solo hay 3 ámbitos en los que se buscará x:

  • Alcance del correo no deseado: contiene todo lo definido en el código 3 y el código 5 (así como el código 4, su variable de bucle)

  • El alcance global: que contiene todo lo definido en el código 1, así como Foo (y lo que cambie después)

  • El espacio de nombres incorporado. Un poco de un caso especial: contiene las diversas funciones y tipos incorporados de Python, como len () y str (). En general, esto no debe ser modificado por ningún código de usuario, por lo tanto, espere que contenga las funciones estándar y nada más.

Solo aparecen más ámbitos cuando introduce una función anidada (o lambda) en la imagen. Sin embargo, se comportarán más o menos como cabría esperar. La función anidada puede acceder a todo en el ámbito local, así como a cualquier cosa en el ámbito de la función de cierre. p.ej.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Restricciones

Se puede acceder a las variables en ámbitos que no sean las variables de la función local, pero no se pueden rebotar a nuevos parámetros sin más sintaxis. En cambio, la asignación creará una nueva variable local en lugar de afectar la variable en el ámbito primario. Por ejemplo:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Para modificar realmente los enlaces de las variables globales desde el alcance de una función, debe especificar que la variable es global con la palabra clave global. P.ej:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Actualmente no hay forma de hacer lo mismo para las variables al incluir ámbitos de funciones , pero Python 3 introduce una nueva palabra clave, " nonlocal" que actuará de manera similar a global, pero para ámbitos de funciones anidadas.

Brian
fuente
111

No hubo una respuesta completa sobre el tiempo de Python3, así que hice una respuesta aquí. La mayor parte de lo que se describe aquí se detalla en la Resolución 4.2.2 de los nombres de la documentación de Python 3.

Como se proporciona en otras respuestas, hay 4 ámbitos básicos, el LEGB, para Local, Enclosing, Global y Builtin. Además de esos, hay un alcance especial, el cuerpo de la clase , que no comprende un alcance cerrado para los métodos definidos dentro de la clase; cualquier tarea dentro del cuerpo de la clase hace que la variable a partir de ahí esté vinculada en el cuerpo de la clase.

Especialmente, ninguna declaración de bloque, además de defy class, crea un alcance variable. En Python 2, la comprensión de la lista no crea un alcance variable, sin embargo, en Python 3 la variable de bucle dentro de las comprensiones de la lista se crea en un nuevo alcance.

Demostrar las peculiaridades del cuerpo de clase.

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Por lo tanto, a diferencia del cuerpo de la función, puede reasignar la variable al mismo nombre en el cuerpo de la clase, para obtener una variable de clase con el mismo nombre; más búsquedas en este nombre resuelven la variable de clase en su lugar.


Una de las mayores sorpresas para muchos recién llegados a Python es que un forbucle no crea un alcance variable. En Python 2, las comprensiones de la lista tampoco crean un alcance (¡mientras que los generadores y las comprensiones de dict sí lo hacen!) En cambio, pierden el valor en la función o el alcance global:

>>> [ i for i in range(5) ]
>>> i
4

Las comprensiones se pueden usar como una manera astuta (o horrible, si lo desea) para hacer variables modificables dentro de las expresiones lambda en Python 2: una expresión lambda crea un alcance variable, como lo defharía la declaración, pero dentro de lambda no se permiten declaraciones. La asignación como una declaración en Python significa que no se permiten asignaciones variables en lambda, pero una comprensión de la lista es una expresión ...

Este comportamiento se ha corregido en Python 3: no hay expresiones de comprensión ni generadores de variables de fuga.


Lo global realmente significa el alcance del módulo; el módulo principal de python es el __main__; todos los módulos importados son accesibles a través de la sys.modulesvariable; para obtener acceso a __main__uno puede usar sys.modules['__main__'], o import __main__; es perfectamente aceptable acceder y asignar atributos allí; se mostrarán como variables en el alcance global del módulo principal.


Si alguna vez se asigna un nombre en el alcance actual (excepto en el alcance de la clase), se considerará que pertenece a ese alcance, de lo contrario se considerará que pertenece a cualquier alcance adjunto que se asigne a la variable (puede que no se asigne todavía, o no), o finalmente el alcance global. Si la variable se considera local, pero aún no se ha establecido o se ha eliminado, la lectura del valor de la variable dará como resultado UnboundLocalErroruna subclase de NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

El alcance puede declarar que quiere explícitamente modificar la variable global (alcance del módulo), con la palabra clave global:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Esto también es posible incluso si se sombreó en el alcance adjunto:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

En python 2 no hay una manera fácil de modificar el valor en el alcance adjunto; generalmente esto se simula teniendo un valor mutable, como una lista con una longitud de 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Sin embargo, en Python 3, nonlocalviene a rescatar:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

La nonlocaldocumentación dice que

Los nombres que figuran en una declaración no local, a diferencia de los que figuran en una declaración global, 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).

es decir, nonlocalsiempre se refiere al ámbito externo no global más interno donde se ha vinculado el nombre (es decir, asignado, incluido el utilizado como la forvariable de destino, en la withcláusula o como un parámetro de función).


Cualquier variable que no se considere local al alcance actual, o cualquier alcance que la incluya, es una variable global. Se busca un nombre global en el diccionario global del módulo; si no se encuentra, el global se busca desde el módulo integrado; el nombre del módulo fue cambiado de python 2 a python 3; en python 2 era __builtin__y en python 3 ahora se llama builtins. Si asigna a un atributo del módulo integrado, será visible a partir de entonces para cualquier módulo como una variable global legible, a menos que ese módulo los sombree con su propia variable global con el mismo nombre.


Leer el módulo integrado también puede ser útil; suponga que desea la función de impresión de estilo python 3 en algunas partes del archivo, pero otras partes del archivo aún usan la printdeclaración. En Python 2.6-2.7 puede obtener la printfunción Python 3 con:

import __builtin__

print3 = __builtin__.__dict__['print']

En from __future__ import print_functionrealidad, no importa la printfunción en ningún lugar de Python 2; en cambio, solo deshabilita las reglas de análisis para la printdeclaración en el módulo actual, se maneja printcomo cualquier otro identificador de variable y, por lo tanto, permite que printla función se busque en las funciones incorporadas.

Antti Haapala
fuente
23

Las reglas de alcance para Python 2.x ya se han descrito en otras respuestas. Lo único que agregaría es que en Python 3.0, también existe el concepto de un alcance no local (indicado por la palabra clave 'no local'). Esto le permite acceder directamente a los ámbitos externos y abre la posibilidad de hacer algunos trucos ingeniosos, incluidos los cierres léxicos (sin hacks feos que involucren objetos mutables).

EDITAR: Aquí está el PEP con más información sobre esto.

Jeremy Cantrell
fuente
23

Un ejemplo un poco más completo de alcance:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

salida:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
brianray
fuente
66
Esta es una gran respuesta. Sin embargo, creo que las diferencias entre methody method_local_refdeben destacarse. methodpuede acceder a la variable global e imprimirla como en 5. Global x. Pero method_local_refno puede porque más adelante define una variable local con ese mismo nombre. Puede probar esto quitando la x = 200línea y ver la diferencia
kiril
@brianray: ¿Qué pasa con z?
Malik A. Rumi
@kiril agregué una nota al respecto
brianray
@ MalikA.Rumi Eliminé z porque no era interesante
brianray
Sorprendentemente, esta es la única explicación clara de los ámbitos de Python, que pude encontrar en todo SO. Simplemente usando un ejemplo muy básico. ¡Gracias!
not2qubit
13

Python resuelve sus variables con, generalmente, tres espacios de nombres disponibles.

En cualquier momento durante la ejecución, hay al menos tres ámbitos anidados cuyos espacios de nombres son directamente accesibles: el ámbito más interno, que se busca primero, contiene los nombres locales; los espacios de nombres de cualquier función de cierre, que se busca comenzando por el alcance de cierre más cercano; el alcance medio, buscado a continuación, contiene los nombres globales del módulo actual; y el ámbito más externo (último buscado) es el espacio de nombres que contiene los nombres integrados.

Hay dos funciones: globalsy localsque le muestran el contenido de dos de estos espacios de nombres.

Los espacios de nombres se crean mediante paquetes, módulos, clases, construcción de objetos y funciones. No hay otros sabores de espacios de nombres.

En este caso, la llamada a una función denominada xdebe resolverse en el espacio de nombres local o en el espacio de nombres global.

Local en este caso, es el cuerpo de la función del método Foo.spam.

Global es, bueno, global.

La regla es buscar en los espacios locales anidados creados por funciones de método (y definiciones de funciones anidadas), luego buscar global. Eso es.

No hay otros ámbitos. La fordeclaración (y otras declaraciones compuestas como ify try) no crean nuevos ámbitos anidados. Solo definiciones (paquetes, módulos, funciones, clases e instancias de objeto).

Dentro de una definición de clase, los nombres son parte del espacio de nombres de clase. code2, por ejemplo, debe estar calificado por el nombre de la clase. En general Foo.code2. Sin embargo, self.code2también funcionará porque los objetos Python miran la clase que lo contiene como una alternativa.

Un objeto (una instancia de una clase) tiene variables de instancia. Estos nombres están en el espacio de nombres del objeto. Deben estar calificados por el objeto. ( variable.instance.)

Desde un método de clase, tienes locales y globales. Dices self.variableelegir la instancia como el espacio de nombres. Notarás que selfes un argumento para cada función miembro de la clase, por lo que es parte del espacio de nombres local.

Consulte las Reglas de Python Scope , Python Scope , Variable Scope .

S.Lott
fuente
55
Esto está fuera de fecha. Desde 2.1 (hace 7 años) hay más de dos ámbitos, ya que las funciones anidadas introducen nuevos ámbitos, por lo que una función dentro de una función tendrá acceso a su ámbito local, el ámbito de las funciones de cierre y el ámbito global (también incorporado).
Brian
Lo siento, este ya no es el caso. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim
9

¿Dónde se encuentra x?

x no se encuentra porque no lo has definido. :-) Se puede encontrar en code1 (global) o code3 (local) si lo pones allí.

code2 (miembros de la clase) no son visibles para el código dentro de los métodos de la misma clase; por lo general, se accede a ellos usando self. code4 / code5 (bucles) viven en el mismo ámbito que code3, por lo que si escribiera en x allí estaría cambiando la instancia de x definida en code3, no creando una nueva x.

Python tiene un alcance estático, por lo que si pasa 'spam' a otra función, el spam todavía tendrá acceso a los globales en el módulo del que proviene (definido en el código 1), y a cualquier otro ámbito que contenga (ver más abajo). Se accedería nuevamente a los miembros de code2 a través de self.

lambda no es diferente a def. Si tiene una lambda utilizada dentro de una función, es lo mismo que definir una función anidada. En Python 2.2 en adelante, los ámbitos anidados están disponibles. En este caso, puede vincular x en cualquier nivel de anidación de funciones y Python recogerá la instancia más interna:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 ve la instancia x desde el ámbito que contiene más cercano, que es el ámbito de función asociado con fun2. Pero las otras instancias x, definidas en fun1 y globalmente, no se ven afectadas.

Antes de nested_scopes, en Python pre-2.1 y en 2.1, a menos que solicite específicamente la función mediante una importación desde el futuro, los alcances de fun1 y fun2 no son visibles para fun3, por lo que la respuesta de S.Lott se mantiene y obtendrá la x global :

0 0
bobince
fuente
1

En Python

cualquier variable a la que se le asigna un valor es local en el bloque en el que aparece la asignación.

Si no se puede encontrar una variable en el alcance actual, consulte el pedido LEGB.

GraceMeng
fuente