J'ai étudié l'évaluation dynamique du code Python et je suis tombé sur les fonctions eval()
et compile()
, ainsi que sur l'instruction exec
.
Quelqu'un peut-il s'il vous plaît expliquer la différence entre eval
et exec
, et comment les différents modes de compile()
s'intègrent?
Fondamentalement, eval
est utilisé pour eval utiliser une seule expression Python générée dynamiquement, et exec
est utilisé pour exec = ute généré Python de manière dynamique, uniquement pour ses effets secondaires.
eval
et exec
ont ces deux différences:
eval
accepte uniquement une expression unique, exec
peut prendre un bloc de code contenant les instructions Python: boucles, try: except:
, class
et fonction/méthode def
initions, etc.
Une expression dans Python est ce que vous pouvez avoir comme valeur dans une affectation de variable:
a_variable = (anything you can put within these parentheses is an expression)
eval
renvoie la valeur de l'expression donnée, alors que exec
ignore la valeur de retour de son code et renvoie toujours None
dans Python 2, il s'agit d'une instruction et ne peut pas être utilisé comme expression, donc ça ne retourne vraiment rien).
Dans les versions 1.0 à 2.7, exec
était une instruction, car CPython devait générer un type d'objet de code différent pour les fonctions qui utilisaient exec
pour ses effets secondaires au sein de la fonction.
Dans Python 3, exec
est une fonction. son utilisation n'a aucun effet sur le bytecode compilé de la fonction où il est utilisé.
Donc fondamentalement:
>>> a = 5
>>> eval('37 + a') # it is an expression
42
>>> exec('37 + a') # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47') # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47') # you cannot evaluate a statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 47
^
SyntaxError: invalid syntax
Le mode compile
dans 'exec'
compile un nombre illimité d'instructions en un bytecode qui renvoie toujours implicitement None
, alors qu'en mode 'eval'
, il compile un _ (singleexpression en un bytecode qui retournela valeur de cette expression).
>>> eval(compile('42', '<string>', 'exec')) # code returns None
>>> eval(compile('42', '<string>', 'eval')) # code returns 42
42
>>> exec(compile('42', '<string>', 'eval')) # code returns 42,
>>> # but ignored by exec
Dans le mode 'eval'
et donc avec la fonction eval
si une chaîne est transmise), compile
lève une exception si le code source contient des instructions ou quoi que ce soit au-delà d'une seule expression:
>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
En fait, l'instruction _ ("eval n'accepte qu'une seule expression"ne s'applique que lorsqu'une chaîne (contenant Python code source) est transmise à eval
. Ensuite, elle est compilée en interne en bytecode à l'aide de compile(source, '<string>', 'eval')
C'est de là que vient la différence.
Si un objet code
qui contient Python _ (bytecode) est passé à exec
ou eval
, ils se comportent de manière identique, sauf que exec
ignore la valeur de retour et renvoie toujours None
. Il est possible d'utiliser eval
pour exécuter quelque chose qui a des instructions, si vous ne faites que compile
d en bytecode avant au lieu de le transmettre sous forme de chaîne:
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
fonctionne sans problème, même si le code compilé contient des instructions. Il retourne toujours None
, car il s'agit de la valeur de retour de l'objet code renvoyé par compile
.
Dans le mode 'eval'
et donc avec la fonction eval
si une chaîne est transmise), compile
lève une exception si le code source contient des instructions ou quoi que ce soit au-delà d'une seule expression:
>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
exec
et eval
La fonction exec
(qui était ne instruction dans Python 2 ) est utilisée pour exécuter une instruction ou un programme créé dynamiquement:
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
La fonction eval
fait la même chose pour une expression unique , _ (etrenvoie la valeur de l'expression:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
et eval
acceptent tous deux que le programme/expression soit exécuté en tant qu'objet str
, unicode
ou bytes
contenant le code source, ou en tant que code
objectqui contient le bytecode Python.
Si un str
/unicode
/bytes
contenant du code source a été passé à exec
, il se comporte de manière équivalente à:
exec(compile(source, '<string>', 'exec'))
et eval
se comporte de la même manière:
eval(compile(source, '<string>', 'eval'))
Puisque toutes les expressions peuvent être utilisées comme instructions dans Python (on les appelle les nœuds Expr
dans le Python grammaire abstraite ; l'inverse n'est pas vrai), vous pouvez toujours utiliser exec
si vous n'avez pas besoin de la valeur de retour. Autrement dit, vous pouvez utiliser eval('my_func(42)')
ou exec('my_func(42)')
, la différence étant que eval
renvoie la valeur renvoyée par my_func
et que exec
le supprime:
>>> def my_func(arg):
... print("Called with %d" % arg)
... return arg * 2
...
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>>
Parmi les 2, seul exec
accepte le code source contenant des instructions telles que def
, for
, while
, import
ou class
, l'instruction d'affectation (a.k.a a = 42
) ou des programmes entiers:
>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
exec
et eval
acceptent tous les deux deux arguments de position supplémentaires - globals
et locals
- qui sont les étendues de variable globale et locale que le code voit. Ces valeurs par défaut sont globals()
et locals()
dans la portée appelée exec
ou eval
, mais tout dictionnaire peut être utilisé pour globals
et tout mapping
pour locals
y compris dict
du cours). Celles-ci peuvent être utilisées non seulement pour restreindre/modifier les variables vues par le code, mais également pour capturer les variables créées par le code exec
uted:
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
(Si vous affichez la valeur de g
entière, ce sera beaucoup plus long, car exec
et eval
ajoutera automatiquement le module intégré sous la forme __builtins__
aux globales s'il le manque).
Dans Python 2, la syntaxe officielle de l'instruction exec
est en fait exec code in globals, locals
, comme dans
>>> exec 'global a; a, b = 123, 42' in g, l
Cependant, la syntaxe alternative exec(code, globals, locals)
a toujours été acceptée aussi (voir ci-dessous).
compile
Le compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
intégré peut être utilisé pour accélérer les invocations répétées du même code avec exec
ou eval
en compilant au préalable la source dans un objet code
. Le paramètre mode
contrôle le type de fragment de code que la fonction compile
accepte et le type de bytecode qu'elle génère. Les choix sont 'eval'
, 'exec'
et 'single'
:
Le mode 'eval'
attend une seule expression et générera un bytecode qui, lorsqu'il sera exécuté, renverra la valeur cette expression:
>>> dis.dis(compile('a + b', '<string>', 'eval'))
1 0 LOAD_NAME 0 (a)
3 LOAD_NAME 1 (b)
6 BINARY_ADD
7 RETURN_VALUE
'exec'
accepte tous les types de constructions python, allant d'expressions uniques à des modules entiers de code, et les exécute comme s'il s'agissait d'instructions de niveau supérieur. L'objet de code retourne None
:
>>> dis.dis(compile('a + b', '<string>', 'exec'))
1 0 LOAD_NAME 0 (a)
3 LOAD_NAME 1 (b)
6 BINARY_ADD
7 POP_TOP <- discard result
8 LOAD_CONST 0 (None) <- load None on stack
11 RETURN_VALUE <- return top of stack
'single'
est une forme limitée de 'exec'
qui accepte un code source contenant une instruction single (ou plusieurs instructions séparées par ;
) si la dernière instruction est une instruction d'expression, le bytecode résultant est également _ (affiche le repr
de la valeur de cette expression sur la sortie standard (!).
Une chaîne if
-Elif
-else
, une boucle avec else
et try
avec ses blocs except
, else
et finally
est considérée comme une instruction unique.
Un fragment source contenant 2 instructions de niveau supérieur est une erreur pour le 'single'
, sauf que dans Python 2, il y a n bugqui autorise parfois plusieurs instructions de niveau supérieur dans le code; seul le premier est compilé; le reste sont ignorés:
Dans Python 2.7.8:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
Et dans Python 3.4.2:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 5
^
SyntaxError: multiple statements found while compiling a single statement
Ceci est très utile pour créer des shells interactifs Python. Cependant, la valeur de l'expression est non renvoyé, même si vous eval
le code résultant.
Ainsi, la plus grande distinction entre exec
et eval
provient en fait de la fonction compile
et de ses modes.
En plus de la compilation du code source en bytecode, compile
prend en charge la compilation arbres à syntaxe abstraitearbres d'analyse de code Python)) en objets code
; et le code source en arbres à syntaxe abstraite (le ast.parse
est écrit en Python et appelle simplement compile(source, filename, mode, PyCF_ONLY_AST)
); ils sont utilisés par exemple pour modifier le code source à la volée, ainsi que pour la création de code dynamique, car il est souvent plus facile de traiter le code comme une arborescence de noeuds au lieu de lignes de texte dans des cas complexes.
Alors que eval
vous permet uniquement d’évaluer une chaîne contenant une seule expression, vous pouvez eval
une instruction entière, voire un module entier qui a été compile
d en bytecode; c'est-à-dire que, avec Python 2, print
est une instruction et ne peut pas être eval
led directement:
>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print("Python is cool")
^
SyntaxError: invalid syntax
compile
avec le mode 'exec'
dans un objet code
et vous pouvez eval
it; la fonction eval
renverra None
.
>>> code = compile('for i in range(3): print("Python is cool")',
'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool
Si l’on cherche dans le code source eval
et exec
dans CPython 3, cela est très évident; ils appellent tous deux PyEval_EvalCode
avec les mêmes arguments, la seule différence étant que exec
RENVOIE EXPLICITEMENT None
.
exec
entre Python 2 et Python 3Une des différences majeures dans Python 2 est que exec
est une instruction et eval
est une fonction intégrée (les deux sont des fonctions intégrées dans Python 3). Il est notoire que la syntaxe officielle de exec
dans Python 2 est exec code [in globals[, locals]]
.
Contrairement à la majorité des Python 2 à 3 portageguidessembleà suggérer , l'instruction exec
dans CPython 2 peut aussi être utilisé avec la syntaxe que _ (looksexactement ressemble à l'invocation de la fonction exec
dans Python 3. La raison en est que Python 0.9.9 avait la exec(code, globals, locals)
built- dans la fonction! Et cette fonction intégrée a été remplacée par l'instruction exec
quelque part avant la version de Python 1. }.
Puisqu'il était souhaitable de ne pas rompre la compatibilité avec Python 0.9.9, Guido van Rossum a ajouté un hack de compatibilité en 199 }: si code
était un tuple de longueur 2 ou 3, et globals
et locals
n’a pas été passé dans la déclaration exec
sinon, le code
serait interprété comme si les deuxième et troisième éléments du tuple étaient respectivement les globals
et locals
. Le piratage de compatibilité n'était pas mentionné même dans la _ { Python 1.4 documentation (la version la plus ancienne disponible en ligne)) }; et de ce fait beaucoup d’écrivains des guides et outils de portage n’étaient pas au courant, jusqu’à ce que ce soit documenté encore en novembre 2012 :
La première expression peut également être un tuple de longueur 2 ou 3. Dans ce cas, les parties facultatives doivent être omises. La forme
exec(expr, globals)
est équivalente àexec expr in globals
, tandis que la formeexec(expr, globals, locals)
est équivalente àexec expr in globals, locals
. La forme Tuple deexec
assure la compatibilité avec Python 3, oùexec
est une fonction plutôt qu'une instruction.
Oui, dans CPython 2.7, on le désigne aisément comme étant une option de compatibilité ascendante (pourquoi confondre les utilisateurs avec le fait qu’il existe une option de compatibilité ascendante), alors qu’elle existait déjà depuis compatibilité ascendante depuis deux décennies.
Ainsi, alors que exec
est une instruction dans Python 1 et Python 2, et une fonction intégrée dans Python 3 et Python 0.9.9,
>>> exec("print(a)", globals(), {'a': 42})
42
a eu le même comportement dans toutes les versions Python largement publiées de tous les temps; et fonctionne dans Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) et IronPython 2.6.1 également (félicitations à leur égard en raison du comportement non documenté de CPython).
Ce que vous ne pouvez pas faire dans Pythons 1.0 - 2.7 avec son hack de compatibilité, est de stocker la valeur de retour de exec
dans une variable:
Python 2.7.11+ (default, Apr 17 2016, 14:00:29)
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
File "<stdin>", line 1
a = exec('print(42)')
^
SyntaxError: invalid syntax
(ce qui ne serait pas utile dans Python 3, car exec
renvoie toujours None
), ou transmettez une référence à exec
:
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
Quel motif que quelqu'un aurait pu utiliser, bien qu'improbable;
Ou utilisez-le dans une liste de compréhension:
>>> [exec(i) for i in ['print(42)', 'print(foo)']
File "<stdin>", line 1
[exec(i) for i in ['print(42)', 'print(foo)']
^
SyntaxError: invalid syntax
ce qui est un abus de compréhension de liste (utilisez plutôt une boucle for
!).
exec
n'est pas une expression: une déclaration dans Python 2.x et une fonction dans Python 3.x. Il compile et évalue immédiatement une instruction ou un ensemble d'instructions contenu dans une chaîne. Exemple:
exec('print(5)') # prints 5.
# exec 'print 5' if you use Python 2.x, nor the exec neither the print is a function there
exec('print(5)\nprint(6)') # prints 5{newline}6.
exec('if True: print(6)') # prints 6.
exec('5') # does nothing and returns nothing.
eval
est une fonction intégrée (not une instruction), qui évalue une expression et renvoie la valeur que cette expression produit. Exemple:
x = eval('5') # x <- 5
x = eval('%d + 6' % x) # x <- 11
x = eval('abs(%d)' % -100) # x <- 100
x = eval('x = 5') # INVALID; assignment is not an expression.
x = eval('if 1: x = 4') # INVALID; if is a statement, not an expression.
compile
est une version de niveau inférieur de exec
et eval
. Il n'exécute ni n'évalue vos instructions ou expressions, mais renvoie un objet code qui peut le faire. Les modes sont les suivants:
compile(string, '', 'eval')
renvoie l'objet code qui aurait été exécuté si vous aviez effectué eval(string)
. Notez que vous ne pouvez pas utiliser des instructions dans ce mode; seule une expression (unique) est valide.compile(string, '', 'exec')
renvoie l'objet code qui aurait été exécuté si vous aviez effectué exec(string)
. Vous pouvez utiliser n'importe quel nombre d'énoncés ici.compile(string, '', 'single')
est comme le mode exec
, mais il ignorera tout sauf la première instruction. Notez qu'une instruction if
/else
avec ses résultats est considérée comme une instruction unique.exec est pour l'instruction et ne renvoie rien. eval est pour expression et renvoie la valeur de expression.
expression signifie "quelque chose" tandis que déclaration signifie "faire quelque chose".