varios variable valor una sentencias resultados nombre mostrar listas lista leer imprimir eliminar elementos como cambiar cadena python variables python-2.7 scope python-internals

valor - Cómo se resuelven las referencias a las variables en Python



mostrar resultados en python (3)

En dos palabras, la diferencia entre el ejemplo 5 y el ejemplo 6 es que en el ejemplo 5 la variable x también se asigna en el mismo ámbito, mientras que no en el ejemplo 6. Esto desencadena una diferencia que puede entenderse por razones históricas.

Esto aumenta UnboundLocalError:

x = "foo" def f(): print x x = 5 f()

en lugar de imprimir "foo". Tiene un poco de sentido, aunque parezca extraño al principio: la función f () define la variable x localmente, incluso si está después de la impresión, por lo que cualquier referencia a x en la misma función debe ser a esa variable local . Al menos tiene sentido, ya que evita sorpresas extrañas si, por error, reutilizó localmente el nombre de una variable global, y está tratando de usar tanto la variable global como la local. Esta es una buena idea porque significa que podemos saber estáticamente, simplemente mirando una variable, qué variable significa. Por ejemplo, sabemos que print x refiere a la variable local (y por lo tanto puede elevar UnboundLocalError) aquí:

x = "foo" def f(): if some_condition: x = 42 print x f()

Ahora, esta regla no funciona para los ámbitos de nivel de clase: allí, queremos que expresiones como x = x funcionen, capturando la variable global x en el ámbito de nivel de clase. Esto significa que los ámbitos de nivel de clase no siguen la regla básica anterior: no podemos saber si x en este ámbito se refiere a alguna variable externa o al x -definido localmente, por ejemplo:

class X: x = x # we want to read the global x and assign it locally bar = x # but here we want to read the local x of the previous line class Y: if some_condition: x = 42 print x # may refer to either the local x, or some global x class Z: for i in range(2): print x # prints the global x the 1st time, and 42 the 2nd time x = 42

Entonces, en los ámbitos de clase, se usa una regla diferente: donde normalmente aumentaría UnboundLocalError --- y solo en ese caso --- en cambio buscará en el módulo global. Eso es todo: no sigue la cadena de ámbitos anidados.

Por qué no? Realmente dudo que haya una mejor explicación que "por razones históricas". En términos más técnicos, podría considerar que la variable x se define localmente en el ámbito de clase (porque está asignada a) y debe pasarse desde el ámbito principal como una variable léxicamente anidada (porque se lee). Sería posible implementarlo utilizando un bytecode diferente de LOAD_NAME que busca en el ámbito local, y vuelve a utilizar la referencia del alcance anidado si no se encuentra.

EDITAR: gracias wilberforce por la referencia a http://bugs.python.org/issue532860 . Es posible que tengamos la oportunidad de reactivar un poco de discusión con el nuevo bytecode propuesto, si creemos que debe ser corregido después de todo (el informe de error considera la eliminación de soporte para x = x pero se cerró por temor a romper demasiado código existente; lo que sugiero aquí sería hacer que x = x funcione en más casos). O puedo estar perdiendo otro punto fino ...

EDIT2: parece que CPython hizo precisamente eso en el tronco actual 3.4: http://bugs.python.org/issue17853 ... ¿o no? Introdujeron el bytecode por una razón ligeramente diferente y no lo usan sistemáticamente ...

Este mensaje es un poco largo con muchos ejemplos, pero espero que nos ayude a mí y a otros a comprender mejor la historia completa de las variables y la búsqueda de atributos en Python 2.7.

Estoy usando los términos de PEP 227 ( http://www.python.org/dev/peps/pep-0227/ ) para bloques de código (como módulos, definición de clases, definiciones de funciones, etc.) y enlaces variables (tales como asignaciones, declaraciones de argumentos, declaración de clase y función, para bucles, etc.)

Estoy usando los términos variables para los nombres que se pueden llamar sin un punto, y los atributos para los nombres que deben calificarse con un nombre de objeto (como obj.x para el atributo x del objeto obj).

Hay tres ámbitos en Python para todos los bloques de código, pero las funciones:

  • Local
  • Global
  • Incorporado

Hay cuatro bloques en Python para las funciones solamente (de acuerdo con PEP 227):

  • Local
  • Funciones adjuntas
  • Global
  • Incorporado

La regla para una variable para vincularla y encontrarla en un bloque es bastante simple:

  • cualquier enlace de una variable a un objeto en un bloque hace que esta variable sea local para este bloque, a menos que la variable se declare global (en ese caso, la variable pertenece al ámbito global)
  • se busca una referencia a una variable usando la regla LGB (local, global, incorporada) para todos los bloques, pero las funciones
  • se busca una referencia a una variable usando la regla LEGB (local, adjuntando, global, incorporada) solo para las funciones.

Avíseme sobre ejemplos que validan esta regla y muestran muchos casos especiales. Para cada ejemplo, daré mi entendimiento. Por favor, corríjame si estoy equivocado. Para el último ejemplo, no entiendo el resultado.

Ejemplo 1:

x = "x in module" class A(): print "A: " + x #x in module x = "x in class A" print locals() class B(): print "B: " + x #x in module x = "x in class B" print locals() def f(self): print "f: " + x #x in module self.x = "self.x in f" print x, self.x print locals() >>>A.B().f() A: x in module {''x'': ''x in class A'', ''__module__'': ''__main__''} B: x in module {''x'': ''x in class B'', ''__module__'': ''__main__''} f: x in module x in module self.x in f {''self'': <__main__.B instance at 0x00000000026FC9C8>}

No hay un ámbito anidado para las clases (regla LGB) y una función en una clase no puede acceder a los atributos de la clase sin utilizar un nombre calificado (self.x en este ejemplo). Esto está bien descrito en PEP227.

ejemplo 2:

z = "z in module" def f(): z = "z in f()" class C(): z = "z in C" def g(self): print z print C.z C().g() f() >>> z in f() z in C

Aquí las variables en funciones se buscan usando la regla LEGB, pero si una clase está en la ruta, los argumentos de la clase se omiten. Aquí nuevamente, esto es lo que PEP 227 está explicando.

ejemplo 3:

var = 0 def func(): print var var = 1 >>> func() Traceback (most recent call last): File "<pyshell#102>", line 1, in <module> func() File "C:/Users/aa/Desktop/test2.py", line 25, in func print var UnboundLocalError: local variable ''var'' referenced before assignment

Esperamos con un lenguaje dinámico como python que todo se resuelva de forma dinámica. Pero este no es el caso para las funciones. Las variables locales se determinan en tiempo de compilación. PEP 227 y http://docs.python.org/2.7/reference/executionmodel.html describen este comportamiento de esta manera

"Si se produce una operación de vinculación de nombre en cualquier lugar dentro de un bloque de código, todos los usos del nombre dentro del bloque se tratan como referencias al bloque actual".

ejemplo 4:

x = "x in module" class A(): print "A: " + x x = "x in A" print "A: " + x print locals() del x print locals() print "A: " + x >>> A: x in module A: x in A {''x'': ''x in A'', ''__module__'': ''__main__''} {''__module__'': ''__main__''} A: x in module

Pero vemos aquí que esta declaración en PEP227 "Si una operación de enlace de nombre ocurre en cualquier lugar dentro de un bloque de código, todos los usos del nombre dentro del bloque se tratan como referencias al bloque actual". es incorrecto cuando el bloque de código es una clase. Además, para las clases, parece que el enlace de nombre local no se realiza en tiempo de compilación, sino durante la ejecución utilizando el espacio de nombres de clase. En ese sentido, PEP227 y el modelo de ejecución en el documento Python es engañoso y, en algunas partes, incorrecto.

ejemplo 5:

x = ''x in module'' def f2(): x = ''x in f2'' def myfunc(): x = ''x in myfunc'' class MyClass(object): x = x print x return MyClass myfunc() f2() >>> x in module

mi comprensión de este código es la siguiente. La instrucción x = x primero busca el objeto al que hace referencia la mano derecha x de la expresión. En ese caso, el objeto se busca localmente en la clase, luego siguiendo la regla LGB se busca en el alcance global, que es la cadena ''x en el módulo''. Entonces se crea un atributo local x a MyClass en el diccionario de clase y se apunta al objeto de cadena.

ejemplo 6:

Ahora aquí hay un ejemplo que no puedo explicar. Está muy cerca del ejemplo 5, solo estoy cambiando el atributo MyClass local de xa y.

x = ''x in module'' def f2(): x = ''x in f2'' def myfunc(): x = ''x in myfunc'' class MyClass(object): y = x print y return MyClass myfunc() f2() >>> x in myfunc

¿Por qué en ese caso la referencia x en MyClass se busca en la función más interna?


En un mundo ideal, estarías en lo cierto y algunas de las inconsistencias que encontraste serían incorrectas. Sin embargo, CPython ha optimizado algunos escenarios, específicamente locales de función. Estas optimizaciones, junto con la forma en que el compilador y el bucle de evaluación interactúan y el precedente histórico, conducen a la confusión.

Python traduce el código a bytecodes, y luego estos son interpretados por un bucle de intérprete. El código de operación ''regular'' para acceder a un nombre es LOAD_NAME , que busca un nombre de variable como lo haría en un diccionario. LOAD_NAME primero buscará un nombre como local y, si eso falla, buscará un nombre global. LOAD_NAME arroja una excepción NameError cuando no se encuentra el nombre.

Para ámbitos anidados, buscar nombres fuera del alcance actual se implementa mediante cierres; si un nombre no está asignado pero está disponible en un ámbito anidado (no global), dichos valores se manejan como un cierre. Esto es necesario porque un ámbito principal puede contener diferentes valores para un nombre dado en diferentes momentos; dos llamadas a una función primaria pueden llevar a diferentes valores de cierre. Así que Python tiene LOAD_CLOSURE MAKE_CLOSURE LOAD_CLOSURE , MAKE_CLOSURE y LOAD_DEREF para esa situación; los dos primeros LOAD_DEREF se utilizan para cargar y crear un cierre para un ámbito anidado, y LOAD_DEREF cargará el valor cerrado cuando el ámbito anidado lo necesite.

Ahora, LOAD_NAME es relativamente lento; consultará dos diccionarios, lo que significa que primero debe hacer clic en la tecla y ejecutar algunas pruebas de igualdad (si el nombre no fue internado). Si el nombre no es local, entonces tiene que hacer esto de nuevo para un global. Para las funciones, que potencialmente se pueden llamar decenas de miles de veces, esto puede volverse tedioso rápidamente. Así que los locales tienen códigos de operación especiales. Cargando un nombre local es implementado por LOAD_FAST , que busca las variables locales por índice en una matriz especial de nombres locales. Esto es mucho más rápido, pero requiere que el compilador primero tenga que ver si un nombre es local y no global. Para poder buscar nombres globales, se usa otro código de operación LOAD_GLOBAL . El compilador optimiza explícitamente en este caso para generar los códigos de operación especiales. LOAD_FAST lanzará una excepción UnboundLocalError cuando aún no haya un valor para el nombre.

Por otro lado, los cuerpos de definición de clase, aunque se tratan como una función, no obtienen este paso de optimización. Las definiciones de clase no están destinadas a ser llamadas con tanta frecuencia; la mayoría de los módulos crean clases una vez , cuando se importan. Los ámbitos de clase tampoco cuentan cuando anidan, por lo que las reglas son más simples. Como resultado, los cuerpos de definición de clase no actúan como funciones cuando comienzas a mezclar un poco los ámbitos.

Por lo tanto, para ámbitos sin función, LOAD_NAME y LOAD_DEREF se utilizan para locales y globales, y para cierres, respectivamente. Para las funciones, LOAD_FAST , LOAD_GLOBAL y LOAD_DEREF se utilizan en su lugar.

Tenga en cuenta que los cuerpos de clase se ejecutan tan pronto como Python ejecuta la línea de class . Por lo tanto, en el ejemplo 1, la class B dentro de la class A se ejecuta tan pronto como se ejecuta la class A , que es cuando se importa el módulo. En el ejemplo 2, C no se ejecuta hasta que se llame a f() , no antes.

Veamos tus ejemplos:

  1. Has anidado una clase AB en una clase A Los cuerpos de clase no forman ámbitos anidados, por lo tanto, aunque el cuerpo de la clase AB se ejecuta cuando se ejecuta la clase A , el compilador usará LOAD_NAME para buscar x . AB().f() es una función (vinculada a la instancia B() como método), por lo que usa LOAD_GLOBAL para cargar x . Aquí ignoraremos el acceso a los atributos, es un patrón de nombre muy bien definido.

  2. Aquí f().Cz está en el ámbito de clase, por lo que la función f().C().g() omitirá el ámbito C y observará el ámbito f() lugar, utilizando LOAD_DEREF .

  3. Aquí el compilador determinó que var era un local porque lo asigna dentro del alcance. Las funciones están optimizadas, por lo que LOAD_FAST se usa para buscar el local y se lanza una excepción.

  4. Ahora las cosas se ponen un poco raras. class A se ejecuta en el alcance de clase, por lo que LOAD_NAME se está utilizando. Ax se eliminó del diccionario local para el alcance, por lo que el segundo acceso a x da como resultado que se encuentre la x global; LOAD_NAME buscó un local primero y no lo encontró allí, volviendo a la búsqueda global.

    Sí, esto parece ser inconsistente con la documentación. Python-the-language y CPython-la implementación están chocando un poco aquí. Sin embargo, estás empujando los límites de lo que es posible y práctico en un lenguaje dinámico; Comprobar si x debería haber sido un local en LOAD_NAME sería posible, pero lleva un tiempo de ejecución precioso para un caso de esquina que la mayoría de los desarrolladores nunca encontrarán.

  5. Ahora estás confundiendo el compilador. Usaste x = x en el alcance de la clase y, por lo tanto, estás configurando un local de un nombre fuera del alcance. El compilador encuentra que x es un local aquí (lo asigna), por lo que nunca considera que también podría ser un nombre delimitado. El compilador usa LOAD_NAME para todas las referencias a x en este ámbito, porque este no es un cuerpo de función optimizado.

    Al ejecutar la definición de clase, x = x primero requiere que busque x , por lo que usa LOAD_NAME para hacerlo. No se define x , LOAD_NAME no encuentra un local, por lo que se encuentra la x global . El valor resultante se almacena como local, que también se denomina x . print x usa LOAD_NAME nuevamente, y ahora encuentra el nuevo valor de x local.

  6. Aquí no confundiste el compilador. Está creando un y local, x no es local, por lo que el compilador lo reconoce como un nombre de ámbito de la función primaria f2().myfunc() . x se busca con LOAD_DEREF desde el cierre, y se almacena en y .

Podrías ver la confusión entre 5 y 6 como un error, aunque uno que no vale la pena arreglar en mi opinión. Ciertamente fue archivado como tal, vea el http://bugs.python.org/issue532860 en el rastreador de http://bugs.python.org/issue532860 de Python, ha estado allí por más de 10 años.

El compilador podría buscar un nombre delimitado x incluso cuando x también es local, para esa primera asignación en el ejemplo 5. O LOAD_NAME podría verificar si el nombre es realmente local, y arrojar un UnboundLocalError si no se encontró un local , a expensas de un mayor rendimiento. Si esto hubiera estado dentro del alcance de una función, LOAD_FAST se habría utilizado para el ejemplo 5, y se UnboundLocalError un UnboundLocalError inmediatamente.

Sin embargo, como se muestra el error de referencia, por razones históricas, se conserva el comportamiento. Probablemente hoy haya un código que se romperá si se soluciona este error.


Para abreviar, este es un caso de esquina del alcance de Python que es un poco inconsistente, pero tiene que mantenerse para la compatibilidad con versiones anteriores (y porque no está tan claro cuál debería ser la respuesta correcta). Puede ver mucha discusión original al respecto en la lista de correo de Python cuando se estaba implementando PEP 227, y algunos en el http://bugs.python.org/issue532860 para el cual este comportamiento es la solución.

Podemos averiguar por qué hay una diferencia al usar el módulo dis , que nos permite mirar dentro de los objetos de código para ver el bytecode en el que se ha compilado un fragmento de código. Estoy en Python 2.6, por lo que los detalles de esto pueden ser ligeramente diferentes, pero veo el mismo comportamiento, así que creo que es probablemente lo suficientemente cerca de 2.7.

El código que inicializa cada MyClass anidado vive en un objeto de código al que puede acceder a través de los atributos de las funciones de nivel superior. (Estoy renombrando las funciones del ejemplo 5 y el ejemplo 6 a f1 y f2 respectivamente.)

El objeto de código tiene una tupla co_consts , que contiene el objeto de código myfunc , que a su vez tiene el código que se ejecuta cuando se crea MyClass :

In [20]: f1.func_code.co_consts Out[20]: (None, ''x in f2'', <code object myfunc at 0x1773e40, file "<ipython-input-3-6d9550a9ea41>", line 4>) In [21]: myfunc1_code = f1.func_code.co_consts[2] In [22]: MyClass1_code = myfunc1_code.co_consts[3] In [23]: myfunc2_code = f2.func_code.co_consts[2] In [24]: MyClass2_code = myfunc2_code.co_consts[3]

Luego puede ver la diferencia entre ellos en bytecode usando dis.dis :

In [25]: from dis import dis In [26]: dis(MyClass1_code) 6 0 LOAD_NAME 0 (__name__) 3 STORE_NAME 1 (__module__) 7 6 LOAD_NAME 2 (x) 9 STORE_NAME 2 (x) 8 12 LOAD_NAME 2 (x) 15 PRINT_ITEM 16 PRINT_NEWLINE 17 LOAD_LOCALS 18 RETURN_VALUE In [27]: dis(MyClass2_code) 6 0 LOAD_NAME 0 (__name__) 3 STORE_NAME 1 (__module__) 7 6 LOAD_DEREF 0 (x) 9 STORE_NAME 2 (y) 8 12 LOAD_NAME 2 (y) 15 PRINT_ITEM 16 PRINT_NEWLINE 17 LOAD_LOCALS 18 RETURN_VALUE

Entonces, la única diferencia es que en MyClass1 , x se carga usando LOAD_NAME , mientras que en MyClass2 , se carga usando LOAD_DEREF . LOAD_DEREF busca un nombre en un ámbito adjunto, por lo que obtiene ''x in myfunc''. LOAD_NAME no sigue los ámbitos anidados: como no puede ver los nombres de x enlazados en myfunc o f1 , obtiene el enlace de nivel de módulo.

Entonces la pregunta es, ¿por qué el código de las dos versiones de MyClass se compila en dos códigos de MyClass diferentes? En f1 el enlace está sombreando x en el alcance de la clase, mientras que en f2 está vinculando un nuevo nombre. Si los ámbitos MyClass fueran funciones anidadas en lugar de clases, la línea y = x en f2 se compilaría de la misma manera, pero x = x en f1 sería LOAD_FAST , esto es porque el compilador sabría que x está vinculado a la función , por lo que debe usar LOAD_FAST para recuperar una variable local. Esto fallaría con UnboundLocalError cuando se llamó.

In [28]: x = ''x in module'' def f3(): x = ''x in f2'' def myfunc(): x = ''x in myfunc'' def MyFunc(): x = x print x return MyFunc() myfunc() f3() --------------------------------------------------------------------------- Traceback (most recent call last) <ipython-input-29-9f04105d64cc> in <module>() 9 return MyFunc() 10 myfunc() ---> 11 f3() <ipython-input-29-9f04105d64cc> in f3() 8 print x 9 return MyFunc() ---> 10 myfunc() 11 f3() <ipython-input-29-9f04105d64cc> in myfunc() 7 x = x 8 print x ----> 9 return MyFunc() 10 myfunc() 11 f3() <ipython-input-29-9f04105d64cc> in MyFunc() 5 x = ''x in myfunc'' 6 def MyFunc(): ----> 7 x = x 8 print x 9 return MyFunc() UnboundLocalError: local variable ''x'' referenced before assignment

Esto falla porque la función MyFunc luego usa LOAD_FAST :

In [31]: myfunc_code = f3.func_code.co_consts[2] MyFunc_code = myfunc_code.co_consts[2] In [33]: dis(MyFunc_code) 7 0 LOAD_FAST 0 (x) 3 STORE_FAST 0 (x) 8 6 LOAD_FAST 0 (x) 9 PRINT_ITEM 10 PRINT_NEWLINE 11 LOAD_CONST 0 (None) 14 RETURN_VALUE

(Por otro lado, no es una gran sorpresa que haya una diferencia en cómo el alcance interactúa con el código en el cuerpo de las clases y el código en una función. Puede decirlo porque las vinculaciones en el nivel de clase no están disponibles en los métodos: los ámbitos de los métodos no están anidados dentro del ámbito de la clase de la misma forma que las funciones anidadas. Tiene que llegar explícitamente a ellos a través de la clase, o utilizando self. (que caerá en la clase si no hay un nivel de instancia) Unión).)