round example python python-3.x exec locals

example - repr python



¿Cómo funciona el ejecutivo con los locales? (3)

Pensé que esto imprimiría 3, pero imprime 1:

def f(): a = 1 exec("a = 3") print(a)


Este problema se discute un poco en la lista de errores de Python3 . En última instancia, para obtener este comportamiento, necesita hacer:

def foo(): ldict = {} exec("a=3",globals(),ldict) a = ldict[''a''] print(a)

Y si verifica la documentación de Python3 en exec , verá la siguiente nota:

Los locales predeterminados actúan como se describe para la función locals() continuación: no se deben intentar modificaciones al diccionario local predeterminado. Pase un diccionario de locales explícito si necesita ver los efectos del código en los locales después de que devuelve la función exec ().

Refiriéndose a un mensaje específico en el informe de error , Georg Brandl dice:

Modificar los locales de una función sobre la marcha no es posible sin varias consecuencias: normalmente, los locales de funciones no se almacenan en un diccionario, sino en una matriz , cuyos índices se determinan en el momento de la compilación a partir de los locales conocidos. Esto choca al menos con nuevos locales agregados por exec. La antigua declaración de exec evitó esto, porque el compilador sabía que si se producía un exec sin argumentos globales / locales en una función, ese espacio de nombres sería "no optimizado", es decir, no utilizar la matriz locals. Como exec () es ahora una función normal, el compilador no sabe a qué "exec" puede estar vinculado, y por lo tanto, no se puede tratar, especialmente .

El énfasis es mío.

De modo que lo esencial es que Python3 puede optimizar mejor el uso de variables locales al no permitir este comportamiento de forma predeterminada.

Y en aras de la integridad, como se menciona en los comentarios anteriores, esto funciona como se espera en Python 2.X:

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) [GCC 4.3.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> def f(): ... a = 1 ... exec "a=3" ... print a ... >>> f() 3


La razón por la cual no puede cambiar las variables locales dentro de una función usando exec de esa manera, y la razón por la cual exec actúa como lo hace, puede resumirse como sigue:

  1. exec es una función que comparte su alcance local con el alcance del alcance más interno en el que se llama.
  2. Cuando defina un nuevo objeto dentro del alcance de una función, estará accesible en su espacio de nombres local, es decir, modificará el diccionario local() . Cuando define un nuevo objeto en exec lo que hace es aproximadamente equivalente a lo siguiente:

from copy import copy class exec_type: def __init__(self, *args, **kwargs): # default initializations # ... self.temp = copy(locals()) def __setitem__(self, key, value): if var not in locals(): set_local(key, value) self.temp[key] = value

temp es un espacio de nombres temporal que se restablece después de cada instanciación (cada vez que llama al exec ).

  1. Python comienza a buscar los nombres del espacio de nombres local. Se conoce como la manera de LEGB. Python se inicia desde el espacio de nombres local, luego busca en los ámbitos de Enclosing, luego en Global y al final busca los nombres dentro del espacio de nombres de Buit-in.

Un ejemplo más completo sería algo como lo siguiente:

g_var = 5 def test(): l_var = 10 print(locals()) exec("print(locals())") exec("g_var = 222") exec("l_var = 111") exec("print(locals())") exec("l_var = 111; print(locals())") exec("print(locals())") print(locals()) def inner(): exec("print(locals())") exec("inner_var = 100") exec("print(locals())") exec("print([i for i in globals() if ''__'' not in i])") print("Inner function: ") inner() print("-------" * 3) return (g_var, l_var) print(test()) exec("print(g_var)")

Salida:

{''l_var'': 10} {''l_var'': 10}

los locales son los mismos

{''l_var'': 10, ''g_var'': 222}

después de agregar g_var y cambiar la l_var , solo agrega g_var y deja la l_var sin cambios

{''l_var'': 111, ''g_var'': 222}

l_var ha cambiado porque estamos cambiando e imprimiendo los locales en una instanciación (una llamada a exec)

{''l_var'': 10, ''g_var'': 222} {''l_var'': 10, ''g_var'': 222}

Tanto en las funciones locales como en el exec, el l_var local l_var se modifica y se agrega g_var

Inner function: {} {''inner_var'': 100} {''inner_var'': 100}

inner_function la función inner_function es la misma que la local del ejecutivo.

[''g_var'', ''test'']

global solo contiene g_var y el nombre de la función (después de excluir los métodos especiales)

--------------------- (5, 10) 5


Si estás dentro de un método, puedes hacerlo:

class Thing(): def __init__(self): exec(''self.foo = 2'') x = Thing() print(x.foo)

Puedes leer más sobre esto aquí