Breve descripción de las reglas de scope?

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

Si tengo algún código:

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

¿Dónde se encuentra x ? 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 de bucle
  5. Dentro del bucle for

También está el contexto durante la ejecución, cuando la función spam se pasa a otra parte. ¿Y tal vez las funciones lambda pasan un poco diferente?

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

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

Regla LEGB.

L , Local: nombres asignados de cualquier manera dentro de una función ( def o lambda ) y no declarados globalmente en esa función.

E , Enclosing-function locals: nombre en el ámbito local de todas y cada una de las funciones que incluyen estáticamente ( def o lambda ), desde el interior al exterior.

G , Global (módulo): nombres asignados en el nivel superior de un archivo de módulo, o ejecutando una statement global en una def dentro del archivo.

B , incorporado (Python): nombres preasignados en el módulo de nombres integrados: open , range , SyntaxError , SyntaxError , …

Así, en el caso de

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

El bucle for no tiene su propio espacio de nombres. En orden de LEGB, los scopes serían

L: local, en def spam (en code3 , code 4 , code5 ).

E: función cerrada, cualquier función adjunta (si el ejemplo completo estuviera en otra def )

G: Global. ¿Hubo alguna x declarada globalmente en el módulo ( code1 )?

B: Cualquier x incorporado en Python.

x nunca se encontrará en el code2 (incluso en los casos en los que podría esperarlo, vea la respuesta de Antti o aquí ).

Esencialmente, lo único en Python que introduce un nuevo scope es una definición de función. Las clases son un caso especial, ya que todo lo que se define directamente en el cuerpo se coloca en el espacio de nombres de la clase, pero no se puede acceder 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 spam: contiene todo lo definido en code3 y code5 (así como code4, su variable de bucle)

  • El ámbito global: contiene todo lo definido en code1, así como Foo (y cualquier cambio que se produzca 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 (). Por lo general, esto no debe ser modificado por ningún código de usuario, así que espere que contenga las funciones estándar y nada más.

Más ámbitos solo aparecen cuando introduce una función anidada (o lambda) en la imagen. Sin embargo, estos se comportarán como usted esperaría. La función anidada puede acceder a todo en el ámbito local, así como a cualquier cosa en el ámbito de la función que lo contiene. 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 variables en ámbitos diferentes a las variables de la función local, pero no se pueden recuperar a nuevos parámetros sin más syntax. En cambio, la asignación creará una nueva variable local en lugar de afectar la variable en el ámbito principal. 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 ámbito 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 manera de hacer lo mismo para las variables en los ámbitos de funciones de cierre, pero Python 3 introduce una nueva palabra clave, ” nonlocal “, que actuará de forma similar a la global, pero para los ámbitos de función nesteds.

No hubo una respuesta completa con respecto al tiempo de Python3, así que hice una respuesta aquí.

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 ámbito especial, el cuerpo de la clase , que no comprende un ámbito de cierre para los métodos definidos dentro de la clase; cualquier asignación dentro del cuerpo de la clase hace que la variable a partir de allí sea unida al cuerpo de la clase.

Especialmente, ninguna statement de bloque, además de def y class , crea un ámbito variable. En Python 2, una lista de comprensión no crea un ámbito de variables, sin embargo, en Python 3 la variable de bucle dentro de listas de comprensión se crea en un nuevo ámbito.

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; las búsquedas adicionales en este nombre se resuelven a la variable de clase en su lugar.


Una de las mayores sorpresas para muchos recién llegados a Python es que un bucle for no crea un ámbito variable. En Python 2, las comprensiones de la lista tampoco crean un ámbito (mientras que los generadores y las comprensiones de dictado sí lo hacen).

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

Las comprensiones se pueden usar como una forma astuta (o terrible) de hacer variables modificables dentro de las expresiones lambda en Python 2: una expresión lambda crea un ámbito variable, como lo haría la statement def , pero dentro de lambda no se permiten declaraciones. La asignación de una statement en Python significa que no se permiten asignaciones de variables en lambda, pero una comprensión de lista es una expresión …

Este comportamiento se ha corregido en Python 3: no hay expresiones de comprensión o las variables de generación de fugas.


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


Si alguna vez se asigna un nombre en el ámbito actual (excepto en el ámbito de clase), se considerará que pertenece a ese ámbito; de lo contrario, se considerará que pertenece a cualquier ámbito adjunto que se asigne a la variable (es posible que no se asigne) sin embargo, o no), o finalmente el scope 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 UnboundLocalError , que es una subclase de NameError .

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

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

 x = 5 def foobar(): global x print(x) # -> 5 x += 1 foobar() print(x) # -> 6 

Esto también es posible incluso si estaba sombreado en el ámbito de cierre:

 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() # -> print 5 911 print(x, y) # -> 6 13 

En Python 2, no hay una manera fácil de modificar el valor en el ámbito de cierre; por lo general, esto se simula al tener 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, el nonlocal viene 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. 

Cualquier variable que no se considere local al scope actual, o cualquier scope adjunto, es una variable global. Se busca un nombre global en el módulo diccionario global; si no se encuentra, el global se busca desde el módulo builtins; el nombre del módulo se cambió de python 2 a python 3; en python 2 era __builtin__ y en python 3 ahora se llama builtins . Si asigna un atributo a un módulo incorporado, será visible posteriormente a cualquier módulo como una variable global legible, a menos que ese módulo los oculte con su propia variable global con el mismo nombre.


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

 import __builtin__ print3 = __builtin__.__dict__['print'] 

La función from __future__ import print_function realidad no importa la función de print en cualquier lugar en Python 2, sino que simplemente deshabilita las reglas de análisis para print statement de print en el módulo actual, manejando la print como cualquier otro identificador de variable, y permitiendo así que se print la función de impresión. en los builtins.

Las reglas de scope 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 ámbito no local (indicado por la palabra clave ‘no local’). Esto le permite acceder a los ámbitos externos directamente, y abre la capacidad de hacer algunos trucos, incluidos los cierres léxicos (sin trucos feos que involucren objetos mutables).

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

Un ejemplo un poco más completo de scope:

 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 

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

En cualquier momento durante la ejecución, hay al menos tres ámbitos nesteds 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 buscan a partir del scope de cierre más cercano; el scope medio, que se busca a continuación, contiene los nombres globales del módulo actual; y el scope más externo (buscado en último lugar) es el espacio de nombres que contiene nombres integrados.

Hay dos funciones: globals y locals que le muestran el contenido de dos de estos espacios de nombres.

Los espacios de nombres son creados por 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 llamada x debe 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 los espacios locales nesteds creados por las funciones del método (y las definiciones de funciones anidadas), luego buscar global. Eso es.

No hay otros ámbitos. La instrucción for (y otras declaraciones compuestas como if y try ) no crean nuevos ámbitos nesteds. Solo definiciones (paquetes, módulos, funciones, clases e instancias de objetos).

Dentro de una definición de clase, los nombres son parte del espacio de nombres de clase. code2 , por ejemplo, debe ser calificado por el nombre de la clase. Generalmente Foo.code2 . Sin embargo, self.code2 también funcionará porque los objetos de Python consideran que la clase contenedora es una self.code2 .

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

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

Consulte Reglas de scope de Python , Alcance de Python , Alcance de variable .

¿Dónde se encuentra x?

x no se encuentra como no lo has definido. 🙂 Se puede encontrar en code1 (global) o code3 (local) si lo pones ahí.

El código 2 (miembros de la clase) no es visible para el código dentro de los métodos de la misma clase, por lo general, se accede a ellos usando self. code4 / code5 (loops) vive en el mismo ámbito que code3, por lo que si escribes a x allí, estarías cambiando la instancia x definida en code3, sin crear una nueva x.

Python tiene un ámbito estático, por lo que si pasa el correo no deseado a otra función, el correo basura seguirá teniendo acceso a los elementos globales en el módulo del que proviene (definido en el código 1), y a cualquier otro ámbito que lo contenga (consulte a continuación). Los miembros de code2 serían accedidos nuevamente a través de uno mismo.

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 nesteds están disponibles. En este caso, puede enlazar x en cualquier nivel de anidamiento 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 scope que contiene más cercano, que es el scope de la función asociado con fun2. Pero las otras x instancias, 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 característica usando una importación desde el futuro – fun1 y fun2 los ámbitos no son visibles para fun3, así que la respuesta de S.Lott se mantiene y obtendría la x global :

 0 0