Je pensais que ce imprimerait 3, mais il imprime 1:Comment exec travaille avec les locaux?
def f():
a = 1
exec("a = 3")
print(a)
Je pensais que ce imprimerait 3, mais il imprime 1:Comment exec travaille avec les locaux?
def f():
a = 1
exec("a = 3")
print(a)
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
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
@MarkRushakoff J'obtiens une erreur avec votre implémentation à la ligne d'exec: TypeError: l'objet 'dict' n'est pas callable – Leo
@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. –
Si vous êtes à l'intérieur d'une méthode, vous pouvez le faire:
class Thing():
def __init__(self):
exec('self.foo = 2')
x = Thing()
print(x.foo)
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:
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.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
)
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
Quelle version Python? Est-ce 2.6? –
Imprime 3 sur ma machine avec python 2.5.4 – geoffspear
Même ici, 2.4.5. –