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 def
y 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 for
bucle 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 def
harí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.modules
variable; 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 UnboundLocalError
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 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, 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.
La nonlocal
documentació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, nonlocal
siempre se refiere al ámbito externo no global más interno donde se ha vinculado el nombre (es decir, asignado, incluido el utilizado como la for
variable de destino, en la with
clá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 print
declaración. En Python 2.6-2.7 puede obtener 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 ningún lugar de Python 2; en cambio, solo deshabilita las reglas de análisis para la print
declaración en el módulo actual, se maneja print
como cualquier otro identificador de variable y, por lo tanto, permite que print
la función se busque en las funciones incorporadas.