Considere el siguiente fragmento de código python:
x = 1 class Foo: x = 2 def foo(): x = 3 class Foo: print(x) # prints 3 Foo.foo()Como era de esperar, esto imprime 3. Pero, si agregamos una sola línea al fragmento anterior, el comportamiento cambia:
x = 1 class Foo: x = 2 def foo(): x = 3 class Foo: x += 10 print(x) # prints 11 Foo.foo()Y, si cambiamos el orden de las dos líneas en el ejemplo anterior, el resultado cambia una vez más:
x = 1 class Foo: x = 2 def foo(): x = 3 class Foo: print(x) # prints 1 x += 10 Foo.foo() Me gustaría entender por qué ocurre esto y, de manera más general, comprender las reglas de alcance que causan este comportamiento. De la regla de alcance LEGB, esperaría que ambos fragmentos impriman 3, 13 y 3, ya que hay una x definida en la función foo() .
El alcance del bloque de clases es especial. Está documentado aquí :
Una definición de clase es una declaración ejecutable que puede usar y definir nombres. Estas referencias siguen las reglas normales para la resolución de nombres con la excepción de que las variables locales independientes se buscan en el espacio de nombres global. El espacio de nombres de la definición de clase se convierte en el diccionario de atributos de la clase. El alcance de los nombres definidos en un bloque de clase se limita al bloque de clase; no se extiende a los bloques de código de métodos; esto incluye comprensiones y expresiones generadoras, ya que se implementan utilizando un alcance de función.
Básicamente, los bloques de clase no "participan" en la creación/uso de ámbitos adjuntos.
Entonces, en realidad es el primer ejemplo que no funciona como se documenta . Creo que esto es un error real.
EDITAR:
Bien, en realidad, aquí hay más documentación relevante del modelo de datos , creo que todo es consistente con la documentación:
El cuerpo de la clase se ejecuta (aproximadamente) como exec(body, globals(), namespace). La diferencia clave con respecto a una llamada normal a exec() es que el ámbito léxico permite que el cuerpo de la clase (incluidos los métodos) haga referencia a nombres de los ámbitos actual y externo cuando la definición de la clase se produce dentro de una función.
Entonces , los bloques de clase participan en el uso de ámbitos adjuntos, pero para variables libres (como es normal de todos modos). En la primera parte de la documentación que estoy citando, la parte sobre "las variables locales independientes se buscan en el espacio de nombres global" se aplica a las variables que normalmente el compilador marcaría como locales . Entonces, considere este notorio error, por ejemplo:
x = 1 def foo(): x += 1 print(x) foo()Lanzaría un error local no vinculado, pero una definición de clase equivalente:
x = 1 class Foo: x += 1 print(x) imprimirá 2 .
Básicamente, si hay una declaración de asignación en cualquier parte de un bloque de clase, es "local", pero verificará en el alcance global si hay un local no vinculado en lugar de generar el error UnboundLocal .
Por lo tanto, en su primer ejemplo, no es una variable local, es simplemente una variable libre y la resolución sigue las reglas normales. En los siguientes dos ejemplos, usa una declaración de asignación, marcando x como "local" y, por lo tanto, se buscará en el espacio de nombres global en caso de que no esté vinculado en el local.