Le code suivant donne une sortie différente dans Python2
Et dans Python3
:
from sys import version
print(version)
def execute(a, st):
b = 42
exec("b = {}\nprint('b:', b)".format(st))
print(b)
a = 1.
execute(a, "1.E6*a")
Python2
Imprime:
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
('b:', 1000000.0)
1000000.0
Python3
Imprime:
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
Pourquoi Python2
Lie la variable b
à l'intérieur de la fonction execute
aux valeurs de la chaîne de la fonction exec
, alors que Python3
Ne fonctionne pas fais pas ça? Comment puis-je obtenir le comportement de Python2
Dans Python3
? J'ai déjà essayé de passer des dictionnaires pour les globaux et les locaux à la fonction exec
dans Python3
, Mais rien n'a fonctionné jusqu'à présent.
--- MODIFIER ---
Après avoir lu la réponse de Martijns, j'ai analysé cela avec Python3
. Dans l'exemple suivant, je donne la dictionnaire locals()
en tant que d
à exec
, mais d['b']
Imprime autre chose que la simple impression de b
.
from sys import version
print(version)
def execute(a, st):
b = 42
d = locals()
exec("b = {}\nprint('b:', b)".format(st), globals(), d)
print(b) # This prints 42
print(d['b']) # This prints 1000000.0
print(id(d) == id(locals())) # This prints True
a = 1.
execute(a, "1.E6*a")
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
1000000.0
True
La comparaison des identifiants de d
et locals()
montre qu'ils sont le même objet. Mais dans ces conditions, b
devrait être identique à d['b']
. Qu'est-ce qui ne va pas dans mon exemple?
Il y a une grande différence entre exec
in Python 2 et exec()
in Python 3. Vous traitez exec
en tant que fonction, mais c'est vraiment une instruction in Python 2.
En raison de cette différence, vous ne pouvez pas modifier les variables locales dans la portée de la fonction dans Python 3 en utilisant exec
, même si cela était possible dans Python 2 Pas même des variables précédemment déclarées.
locals()
ne reflète que les variables locales dans une direction. Les éléments suivants n'ont jamais fonctionné en 2 ou 3:
def foo():
a = 'spam'
locals()['a'] = 'ham'
print(a) # prints 'spam'
Dans Python 2, l'utilisation de l'instruction exec
signifiait que le compilateur savait désactiver les optimisations de portée locale (en passant de LOAD_FAST
À LOAD_NAME
Pour exemple, pour rechercher des variables dans les étendues locale et globale.) Avec exec()
étant une fonction, cette option n'est plus disponible et les étendues de fonction sont désormais toujours optimisées.
De plus, dans Python 2, l'instruction exec
copie explicitement toutes les variables trouvées dans locals()
vers les fonctions locales en utilisant PyFrame_LocalsToFast
, Mais uniquement si aucun paramètre globals et locals n'a été fourni.
La solution de contournement appropriée consiste à utiliser un nouvel espace de noms (un dictionnaire) pour votre appel exec()
:
def execute(a, st):
namespace = {}
exec("b = {}\nprint('b:', b)".format(st), namespace)
print(namespace['b'])
La documentation exec()
est très explicite sur cette limitation:
Remarque: La valeur par défaut locaux agit comme décrit pour la fonction
locals()
ci-dessous: modifications de la valeur par défaut locals dictionnaire ne doit pas être tenté. Passez un dictionnaire explicite locals si vous avez besoin de voir les effets du code sur les locaux après le retour de la fonctionexec()
.
Je dirais que c'est un bug de python3.
def u():
exec("a=2")
print(locals()['a'])
u()
imprime "2".
def u():
exec("a=2")
a=2
print(a)
u()
imprime "2".
Mais
def u():
exec("a=2")
print(locals()['a'])
a=2
u()
échoue avec
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in u
KeyError: 'a'
--- EDIT --- Un autre comportement intéressant:
def u():
a=1
l=locals()
exec("a=2")
print(l)
u()
def u():
a=1
l=locals()
exec("a=2")
locals()
print(l)
u()
les sorties
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 1}
Et aussi
def u():
l=locals()
exec("a=2")
print(l)
print(locals())
u()
def u():
l=locals()
exec("a=2")
print(l)
print(locals())
a=1
u()
les sorties
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}}
Apparemment, l'action de exec
sur les locaux est la suivante:
exec
et que cette variable était une variable locale, alors exec
modifie le dictionnaire interne (celui renvoyé par locals()
) et ne le renvoie pas à son état d'origine. Un appel à locals()
met à jour le dictionnaire (comme indiqué dans la section 2 de la documentation python), et la valeur définie dans exec
est oubliée. Le besoin de appeler locals()
pour mettre à jour le dictionnaire n'est pas un bug de python3, car il est documenté, mais il n'est pas intuitif. De plus, le fait que les modifications des locaux dans exec
ne changent pas le les sections locales de la fonction sont une différence documentée avec python2 (la documentation dit "Passez un dictionnaire de sections locales explicite si vous devez voir les effets du code sur les sections locales après le retour de la fonction exec ()"), et je préfère le comportement de python2.exec
et que cette variable n'existait pas auparavant, exec
modifie le dictionnaire interne, sauf si la variable est définie par la suite. Il semble qu'il y ait un bug dans la façon dont locals()
met à jour le dictionnaire; ce bogue donne accès à la valeur définie dans exec
en appelant locals()
après exec
.Résumer:
exec
découle du fait que exec
est une instruction dans Python 2, alors qu'il est devenu une fonction dans Python 3.Notez s'il vous plaît:
Je ne dis rien de nouveau ici. Ceci est juste une Assemblée de la vérité trouvée dans toutes les autres réponses et commentaires. Tout ce que j'essaie ici, c'est de mettre en lumière certains des détails les plus obscurs.
La seule différence entre Python 2 et Python 3 est que, en effet, exec
est capable de changer la portée locale de la fonction englobante) dans Python 2 (car il s'agit d'une instruction et peut accéder à la portée locale actuelle) et ne peut plus le faire dans Python 3 (car il s'agit désormais d'une fonction , s'exécute donc dans sa propre portée locale).
L'irritation, cependant, n'a rien à voir avec l'instruction exec
, elle ne provient que d'un détail de comportement spécial:
locals()
renvoie quelque chose, que je veux appeler "un singleton mutable au niveau de la portée qui, après l'appel à locals()
, ne fait toujours référence qu'à toutes les variables de la portée locale".
Veuillez noter que le comportement de locals()
n'a pas changé entre Python 2 et 3. Donc, ce comportement ainsi que le changement de fonctionnement de exec
ressemble à être erratique, mais ce n'est pas le cas, car cela expose simplement certains détails, qui ont toujours été là.
Que signifie "un singleton mutable de portée qui fait référence à des variables de portée locale"?
scope-wise singleton
, Car quelle que soit la fréquence à laquelle vous appelez locals()
dans la même étendue, l'objet renvoyé est toujours le même. id(d) == id(locals())
, parce que d
et locals()
font référence au même objet, au même singleton, car il ne peut y en avoir qu'un (dans une portée différente vous obtenir un objet différent, mais dans la même portée, vous ne voyez que celui-ci).mutable
, car c'est un objet normal, vous pouvez donc le modifier. locals()
force toutes les entrées de l'objet à référencer à nouveau les variables dans la portée locale.d
), cela modifie l'objet, car il s'agit d'un objet mutable normal.Ces modifications du singleton ne se propagent pas dans la portée locale, car toutes les entrées de l'objet sont references to the variables in the local scope
. Donc, si vous modifiez des entrées, cela modifie l'objet singleton, et non le contenu de l'endroit où "les références pointées avant de modifier la référence" (donc vous ne modifiez pas la variable locale).
En Python, les chaînes et les nombres ne sont pas modifiables. Cela signifie que si vous affectez quelque chose à une entrée, vous ne modifiez pas l'objet vers lequel pointe l'entrée, vous introduisez un nouvel objet et attribuez une référence à celui-ci à l'entrée. Exemple:
a = 1
d = locals()
d['a'] = 300
# d['a']==300
locals()
# d['a']==1
Outre l'optimisation, cela permet:
LOCALS['a']
LOCALS
doit être la portée locale interne)SINGLETON
SINGLETON
, donc il fait référence à toutes les entrées dans LOCALS
SINGLETON
dans LOCALS['d']
d['a']
SINGLETON
est également mis à jour.LOCALS
n'est pas pas mis à jour, donc la variable locale a
ou LOCALS['a']
est toujours Number ( 1)locals()
est de nouveau appelée, SINGLETON
est mis à jour.d
fait référence à SINGLETON
, et non LOCALS
, d
change aussi!Pour en savoir plus sur ce détail surprenant, pourquoi
1
Est un singleton alors que300
Ne l'est pas, voir https://stackoverflow.com/a/30635Mais n'oubliez pas: les nombres sont immuables, donc si vous essayez de changer un nombre en une autre valeur, vous créez effectivement un autre objet.
Conclusion:
Vous ne pouvez pas ramener le comportement exec
de Python 2 à Python 3 (sauf en changeant votre code), car il n'y a aucun moyen de modifier les variables locales en dehors du flux du programme.
Cependant, vous pouvez apporter le comportement de Python 3 à Python 2, de sorte que vous pouvez aujourd'hui écrire des programmes qui s'exécutent de la même manière, qu'ils exécuter avec Python 3 ou Python 2. C'est parce que dans (plus récent) Python 2 vous pouvez utiliser exec
avec également des arguments de fonction (en fait, c'est un 2 ou 3-Tuple), avec permet d'utiliser la même syntaxe avec la même sémantique connue de Python 3:
exec "code"
(qui ne fonctionne que dans Python 2) devient (qui fonctionne pour Python 2 et 3):
exec("code", globals(), locals())
Mais attention, "code"
Ne peut plus altérer la portée de l'enveloppe locale de cette façon. Voir aussi https://docs.python.org/2/reference/simple_stmts.html#exec
Quelques derniers mots:
Le changement de exec
dans Python 3 est bon. En raison de l'optimisation.
Dans Python 2, vous n'avez pas pu optimiser sur exec
, car l'état de toutes les variables locales qui contenaient un contenu immuable pouvait changer de façon imprévisible. Cela ne peut plus se produire. Maintenant, les règles habituelles des invocations de fonctions s'appliquent aussi à exec()
comme à toutes les autres fonctions.
Je crains de ne pas pouvoir l'expliquer exactement, mais cela vient essentiellement du fait que b à l'intérieur de la fonction est local, et que exec()
semble être assigné au b global. Vous devrez déclarer b comme global à l'intérieur de la fonction, et à l'intérieur de l'instruction exec.
Essaye ça:
from sys import version
print(version)
def execute1(a, st):
b = 42
exec("b = {}\nprint('b:', b)".format(st))
print(b)
def execute2(a, st):
global b
b = 42
exec("global b; b = {}\nprint('b:', b)".format(st))
print(b)
a = 1.
execute1(a, "1.E6*a")
print()
execute2(a, "1.E6*a")
print()
b = 42
exec("b = {}\nprint('b:', b)".format('1.E6*a'))
print(b)
Ce qui me donne
3.3.0 (default, Oct 5 2012, 11:34:49)
[GCC 4.4.5]
b: 1000000.0
42
b: 1000000.0
1000000.0
b: 1000000.0
1000000.0
Vous pouvez voir qu'en dehors de la fonction, le b global est automatiquement capté. Dans la fonction, vous imprimez le local b.
Notez que j'aurais pensé que exec()
utilise toujours le b global en premier, de sorte que dans execute2()
, vous n'avez pas besoin de le déclarer dans la fonction exec()
. Mais je trouve que cela ne fonctionne pas (c'est la partie que je ne peux pas expliquer exactement).