Breve descripción de las reglas de alcance
¿Cuáles son exactamente las reglas de alcance de Python?
Si tengo algún código:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
¿ Dónde se x
encuentra? Algunas opciones posibles incluyen la siguiente lista:
- En el archivo fuente adjunto
- En el espacio de nombres de la clase
- En la definición de la función
- En la variable de índice del bucle for
- Dentro del bucle for
También está el contexto durante la ejecución, cuando la función spam
se pasa a otro lugar. ¿ Y tal vez las funciones lambda se ejecutan de manera un poco diferente?
Debe haber una referencia o algoritmo simple en alguna parte. Es un mundo confuso para los programadores intermedios de Python.
En realidad, una regla concisa para la resolución del alcance de Python, de Learning Python, 3rd. Ed. . (Estas reglas son específicas de los nombres de variables, no de los 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 (
def
olambda
) y no declarados globales en esa funciónFunción envolvente : nombres asignados en el ámbito local de todas y cada una de las funciones envolventes estáticas (
def
olambda
), desde el interior al exterior.G lobal (módulo): nombres asignados en el nivel superior de un archivo de módulo o ejecutando una
global
instruccióndef
dentro del archivo.Integrado (Python): nombres preasignados en el módulo de nombres integrado:
open
,range
,SyntaxError
etc.
Entonces, en el caso de
code1
class Foo:
code2
def spam():
code3
for code4:
code5
x()
El for
bucle no tiene su propio espacio de nombres. En orden LEGB, los alcances serían
- L: Local en
def spam
(encode3
,code4
ycode5
) - E: Cualquier función adjunta (si todo el ejemplo estuviera en otro
def
) - G: ¿Hubo alguno
x
declarado globalmente en el módulo (encode1
)? - B: Cualquier función integrada
x
en Python.
x
nunca se encontrará code2
(incluso en los casos en los que se podría esperar que lo haga, consulte la respuesta de Antti o aquí ).
Básicamente, lo único en Python que introduce un nuevo alcance es la definición de una función. Las clases son un caso un poco 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 se puede acceder directamente a ellas desde los métodos (o clases anidadas) que contienen.
En su ejemplo, solo hay 3 ámbitos donde se buscará x:
alcance del spam: contiene todo lo definido en code3 y code5 (así como code4, su variable de bucle)
El alcance global: contiene todo lo definido en el código 1, así como Foo (y cualquier cambio posterior).
El espacio de nombres incorporado. Un caso un poco especial: contiene varias funciones y tipos integrados de Python, como len() y str(). Generalmente esto no debería 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 introduces una función anidada (o lambda) en la imagen. Sin embargo, estos se comportarán más o menos como cabría esperar. La función anidada puede acceder a todo lo que esté en el ámbito local, así como a cualquier cosa en el alcance de la función adjunta. 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 distintos de las variables de la función local, pero no se pueden revertir 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 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 variables globales desde dentro del 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 con las variables en los ámbitos de funciones adjuntas , pero Python 3 introduce una nueva palabra clave, " nonlocal
" que actuará de forma similar a global, pero para ámbitos de funciones anidadas.
No hubo una respuesta completa sobre el tiempo de Python3, así que respondí aquí. La mayor parte de lo que se describe aquí se detalla en 4.2.2 Resolución de nombres de la documentación de Python 3.
Como se proporciona en otras respuestas, hay 4 ámbitos básicos, LEGB, para local, envolvente, global e integrado. Además de estos, existe un ámbito especial, el cuerpo de la clase , que no comprende un ámbito adjunto 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 ahí esté vinculada al cuerpo de la clase.
Especialmente, sin declaración de bloque, además def
y class
, crea un alcance variable. En Python 2, una lista por comprensión no crea un alcance variable; sin embargo, en Python 3, la variable de bucle dentro de la lista por comprensión 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; En su lugar, búsquedas adicionales sobre este nombre se resuelven en la variable de clase.
Una de las mayores sorpresas para muchos recién llegados a Python es que un for
bucle no crea un alcance variable. En Python 2, las listas por comprensión tampoco crean un alcance (¡mientras que los generadores y las comprensiones de dictados sí lo hacen!). En lugar de eso, filtran 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 forma astuta (o horrible si se quiere) de crear variables modificables dentro de expresiones lambda en Python 2: una expresión lambda crea un alcance variable, como lo def
haría la declaración, pero dentro de lambda no se permiten declaraciones. Que la asignación sea una declaración en Python significa que no se permiten asignaciones de variables en lambda, pero una lista por comprensión es una expresión...
Este comportamiento se ha solucionado en Python 3: no hay expresiones de comprensión ni variables que filtren los generadores.
Lo global realmente significa el alcance del módulo; el módulo principal de Python es __main__
; todos los módulos importados son accesibles a través de la sys.modules
variable; para obtener acceso a __main__
uno se puede utilizar sys.modules['__main__']
, o import __main__
; es perfectamente aceptable acceder y asignar atributos allí; aparecerán como variables en el alcance global del módulo principal.
Si alguna vez se asigna un nombre en el ámbito actual (excepto en el ámbito de la 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). todavía, o no en absoluto), o finalmente el alcance global. Si la variable se considera local, pero aún no está configurada 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 x a local variable within the function
# call the function
foobar()
El alcance puede declarar que desea modificar explícitamente 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 un ámbito 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 ámbito adjunto; normalmente 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, nonlocal
viene al rescate:
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 nonlocal
documentación dice que
Los nombres enumerados en una declaración no local, a diferencia de los enumerados en una declaración global, deben hacer referencia a enlaces preexistentes en un ámbito adjunto (el ámbito en el que se debe crear un nuevo enlace no se puede determinar sin ambigüedades).
es decir, nonlocal
siempre se refiere al ámbito no global externo más interno al que se ha vinculado el nombre (es decir, asignado, incluido el uso como for
variable de destino, en la with
cláusula o como parámetro de función).
Cualquier variable que no se considere local en el ámbito actual, o en cualquier ámbito adjunto, es una variable global. Se busca un nombre global en el diccionario global del módulo; si no se encuentra, se busca el global desde el módulo integrado; el nombre del módulo se cambió de python 2 a python 3; en Python 2 lo era __builtin__
y en Python 3 ahora se llama builtins
. Si asigna un atributo del módulo incorporado, 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 incorporado también puede resultar útil; Supongamos 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 print
declaración. En Python 2.6-2.7 puedes hacerte con la print
función Python 3 con:
import __builtin__
print3 = __builtin__.__dict__['print']
En from __future__ import print_function
realidad, no importa la print
función en ninguna parte de Python 2; en cambio, simplemente deshabilita las reglas de análisis para print
la declaración en el módulo actual, manejándola print
como cualquier otro identificador de variable y, por lo tanto, permitiendo que la print
función se busque en las funciones integradas.
Un ejemplo de alcance un poco más completo:
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()
producción:
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