2009-09-23 7 views

Respuesta

27

Este tema se discute tanto en el Python3 bug list. En última instancia, para obtener este comportamiento, que tiene que hacer:

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

Y si marca the Python3 documentation on exec, verá la siguiente nota:

The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

Volviendo a a specific message on the bug report, Georg Brandl dice:

To modify the locals of a function on the fly is not possible without several consequences: normally, function locals are not stored in a dictionary, but an array, whose indices are determined at compile time from the known locales. This collides at least with new locals added by exec. The old exec statement circumvented this, because the compiler knew that if an exec without globals/locals args occurred in a function, that namespace would be "unoptimized", i.e. not using the locals array. Since exec() is now a normal function, the compiler does not know what "exec" may be bound to, and therefore can not treat is specially.

El énfasis es mío.

De modo que la esencia de esto es que Python3 puede optimizar mejor el uso de variables locales por y no por permitiendo este comportamiento por defecto.

Y para la mayor abundamiento, como se ha mencionado en los comentarios anteriores, esto hace 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 
+0

Veo, es un problema con los lugareños() que fue pirateado de Exec en Python 2.X. Este problema no está tan claramente documentado como me hubiera gustado. Los ejecutivos/locales que cambian de 2.X a 3.X deben señalarse en algún lugar http://docs.python.org/3.1/library/functions.html#exec y creo que el ejecutor debe tener un parámetro conveniente que evite esta optimización. .. – ubershmekel

+0

@MarkRushakoff Recibo un error con su implementación en la línea de ejecución: TypeError: el objeto 'dict' no se puede llamar – Leo

+0

@Leo no debería ser 'ldict', no' dict'? De todos modos, ya no trabajo mucho en Python, así que si no es así, con suerte alguien más lo hará. –

0

La razón por la que no se puede cambiar las variables locales dentro de una función utilizando exec de esa manera y por qué exec actuar de la manera que lo hace se puede resumir de la siguiente manera:

  1. exec es una función que comparte su scape local con el alcance del ámbito más interno en el que se llama.
  2. Siempre que 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 se define un nuevo objeto en exec lo que hace es más o menos equivalente a la 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 restablece después de cada instancia. (Cada vez que se llame a la exec)


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

Un ejemplo más amplio que harí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} 
# locals are the same 
{'l_var': 10, 'g_var': 222} 
# after adding g_var and changing the l_var it only adds g_var and left the l_var unchanged 
{'l_var': 111, 'g_var': 222} 
# l_var is changed because we are changing and printing the locals in one instantiation (one call to exec) 
{'l_var': 10, 'g_var': 222} 
{'l_var': 10, 'g_var': 222} 
# In both function's locals and exec's local l_var is unchanged and g_var is added 
Inner function: 
{} 
{'inner_var': 100} 
{'inner_var': 100} 
# inner_function's local is same as exec's local 
['g_var', 'test'] 
# global is only contain g_var and function name (after excluding the special methods) 
--------------------- 

(5, 10) 
5 
Cuestiones relacionadas