2009-09-23 8 views

Répondre

27

Cette question est un peu discuté dans le Python3 bug list. En fin de compte, pour obtenir ce comportement, vous devez faire:

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

Et si vous vérifiez the Python3 documentation on exec, vous verrez la note suivante:

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.

Revenant à a specific message on the bug report, dit Georg Brandl:

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.

L'accent est à moi. Donc, l'essentiel est que Python3 peut mieux optimiser l'utilisation des variables locales par et non par en autorisant ce comportement par défaut.

Et pour être complet, tel que mentionné dans les commentaires ci-dessus, ce ne fonctionne comme prévu 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

Je vois, c'est un problème avec localals() qui a été piraté de exec dans python 2.X. Ce problème n'est pas aussi clairement documenté que je l'aurais souhaité. Exec/Locals passant de 2.X à 3.X devrait être indiqué quelque part http://docs.python.org/3.1/library/functions.html#exec et je pense que exec devrait avoir un paramètre de commodité qui contourne cette optimisation. .. – ubershmekel

+0

@MarkRushakoff J'obtiens une erreur avec votre implémentation à la ligne d'exec: TypeError: l'objet 'dict' n'est pas callable – Leo

+0

@Leo ne devrait-il pas être 'ldict', pas' dict'? Quoi qu'il en soit, je ne travaille plus beaucoup en Python, donc si ce n'est pas le cas, j'espère que quelqu'un d'autre interviendra. –

0

La raison pour laquelle vous ne pouvez pas modifier les variables locales au sein d'une fonction à l'aide exec de cette façon et pourquoi exec agir comme il le fait peut se résumer comme suit:

  1. exec est une fonction qui partage son Scape local avec la portée de la portée la plus intérieure dans laquelle elle est appelée.
  2. Chaque fois que vous définissez un nouvel objet dans la portée d'une fonction, il sera accessible dans son espace de noms local, c'est-à-dire qu'il modifiera le dictionnaire local(). Lorsque vous définissez un nouvel objet dans exec ce qu'il fait est à peu près équivalent à ce qui suit:

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 est un espace de noms temporaire qui remet à zéro après chaque instanciation. (Chaque fois que vous appelez la exec)


  1. Python commence la recherche pour les noms de l'espace de noms local. C'est connu comme la manière LEGB. Python commence à partir de namespce local puis regarde dans les étendues d'Enclosing, puis Global et à la fin il recherche les noms dans l'espace de noms Buit-in.

Un exemple plus complet serait nous quelque chose comme suit:

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)") 

Sortie:

{'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 
Questions connexes