Quelle est la différence entre eval, exec et compile?

428

Je l' ai regardé une évaluation dynamique du code Python, et traversent la eval()et compile()fonctions, et la execdéclaration.

Quelqu'un peut-il expliquer la différence entre evalet exec, et comment les différents modes de compile()s'intégrer?

andrewdotnich
la source

Réponses:

518

La réponse courte, ou TL; DR

En gros, evalest utilisé pour eval Uate une seule expression Python généré dynamiquement, et execest utilisé pour exec ute code Python généré dynamiquement seulement pour ses effets secondaires.

evalet execont ces deux différences:

  1. evalaccepte qu'une seule expression , execpeut prendre un bloc de code qui a des instructions Python: boucles, try: except:, classet la fonction / méthodedef initions de et ainsi de suite.

    Une expression en Python est tout ce que vous pouvez avoir comme valeur dans une affectation de variable:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval renvoie la valeur de l'expression donnée, alors qu'il execignore la valeur de retour de son code, et renvoie toujours None(en Python 2, c'est une instruction et ne peut pas être utilisée comme expression, donc elle ne retourne vraiment rien).

Dans les versions 1.0 - 2.7, execétait une déclaration, car CPython avait besoin de produire un type d'objet de code différent pour les fonctions qui utilisaient execses effets secondaires à l'intérieur de la fonction.

En Python 3, execest une fonction; son utilisation n'a aucun effet sur le bytecode compilé de la fonction où il est utilisé.


Donc en gros:

>>> 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 compilein 'exec'compile n'importe quel nombre d'instructions dans un bytecode qui renvoie implicitement toujours None, tandis que dans le 'eval'mode il compile une seule expression en bytecode qui retourne la 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 'eval'mode (et donc avec la evalfonction si une chaîne est passée), le compilelève une exception si le code source contient des instructions ou autre chose 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" s'applique uniquement lorsqu'une chaîne (qui contient le code source Python ) est passée à eval. Ensuite, il est compilé en interne en bytecode en utilisant compile(source, '<string>', 'eval')C'est de là que vient vraiment la différence.

Si un codeobjet (qui contient le bytecode Python ) est passé à execou eval, ils se comportent de manière identique , à l'exception du fait qu'il execignore la valeur de retour, toujours en retournant Nonetoujours. Il est donc possible d'utiliser evalpour exécuter quelque chose qui a des instructions, si vous venez de le compiled en bytecode avant au lieu de le passer comme une 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 renvoie toujours None, car il s'agit de la valeur de retour de l'objet de code renvoyé parcompile .

Dans le 'eval'mode (et donc avec la evalfonction si une chaîne est passée), le compilelève une exception si le code source contient des instructions ou autre chose 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

La réponse plus longue, alias les détails sanglants

exec et eval

La execfonction (qui était une instruction en 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 evalfonction fait la même chose pour une expression unique , et renvoie la valeur de l'expression:

>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84

execet les evaldeux acceptent que le programme / l'expression soit exécuté en tant que str, unicodeou bytesobjet contenant du code source, ou en tant codequ'objet contenant du bytecode Python.

Si un code source contenant str/ unicode/ a bytesété transmis à exec, il se comporte de manière équivalente à:

exec(compile(source, '<string>', 'exec'))

et evalse comporte de manière similaire à:

eval(compile(source, '<string>', 'eval'))

Étant donné que toutes les expressions peuvent être utilisées comme des instructions en Python (celles-ci sont appelées les Exprnœuds dans la grammaire abstraite Python ; l'inverse n'est pas vrai), vous pouvez toujours les utiliser execsi vous n'avez pas besoin de la valeur de retour. C'est-à-dire que vous pouvez utiliser soit eval('my_func(42)')ou exec('my_func(42)'), la différence étant que evalretourne la valeur retournée par my_funcet la execrejette:

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

Sur les 2, execn'accepte le code source qui contient des déclarations, comme def, for, while, importou class, l'instruction d'affectation (alias 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

Les deux execet evalacceptent 2 arguments positionnels supplémentaires - globalset locals- qui sont les étendues de variables globales et locales que le code voit. Ces valeurs par défaut sont dans globals()et locals()dans la portée qui a appelé execou eval, mais n'importe quel dictionnaire peut être utilisé pour globalset n'importe quel mappingpour locals(y compris dictbien sûr). Ceux-ci peuvent être utilisés non seulement pour restreindre / modifier les variables que le code voit, mais sont également souvent utilisés pour capturer les variables que le execcode utilisé crée:

>>> 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 l'ensemble g, ce serait beaucoup plus long, car execet evalajoutez __builtins__automatiquement le module intégré aux globales s'il est manquant).

En Python 2, la syntaxe officielle de l' execinstruction 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

La fonction compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)intégrée peut être utilisée pour accélérer les invocations répétées du même code avec execou evalen compilant au codepréalable la source dans un objet. Le modeparamètre contrôle le type de fragment de code que la compilefonction accepte et le type de bytecode qu'elle produit. Les choix sont 'eval', 'exec'et 'single':

  • 'eval'mode attend une seule expression et produira un bytecode qui, lors de l'exécution, retournera la valeur de 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, des expressions simples aux modules entiers de code, et les exécute comme s'il s'agissait d'instructions de niveau supérieur de module. L'objet code renvoie 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 'exec'qui accepte un code source contenant une seule déclaration (ou plusieurs instructions séparées par ;) si la dernière déclaration est une déclaration d'expression, le bytecode résultant également imprime la reprde la valeur de cette expression à la sortie standard (!) .

    Une if- elif- elsechaîne, une boucle avec else, et tryavec son except, elseet des finallyblocs est considéré comme une seule instruction.

    Un fragment source contenant 2 instructions de niveau supérieur est une erreur pour le 'single', sauf qu'en Python 2 il y a un bogue qui autorise parfois plusieurs instructions de haut niveau dans le code; seul le premier est compilé; les autres sont ignorés:

    En Python 2.7.8:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    >>> a
    5

    Et en 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 Python interactifs. Cependant, la valeur de l'expression n'est pas retournée , même si vous evalle code résultant.

Ainsi la plus grande distinction execet evalvient en fait de la compilefonction et de ses modes.


En plus de compiler le code source en bytecode, compileprend en charge la compilation d'arbres de syntaxe abstraite (arbres d'analyse du code Python) en codeobjets; et le code source dans des arbres de syntaxe abstraite (le ast.parseest écrit en Python et juste des appels compile(source, filename, mode, PyCF_ONLY_AST)); ceux-ci sont utilisés par exemple pour modifier le code source à la volée, et également pour la création de code dynamique, car il est souvent plus facile de gérer le code comme un arbre de nœuds au lieu de lignes de texte dans des cas complexes.


Alors evalque vous permet uniquement d'évaluer une chaîne qui contient une seule expression, vous pouvez evalune instruction entière, ou même un module entier qui a été compiled en bytecode; c'est-à-dire qu'avec Python 2, printc'est une déclaration et ne peut pas être evaldirigée 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

compileavec le 'exec'mode dans un codeobjet et vous pouvez le eval faire ; la evalfonction reviendra 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 regarde evalet execcode source dans CPython 3, c'est très évident; ils appellent tous les deux PyEval_EvalCodeavec les mêmes arguments, la seule différence étant qu'ils execretournent explicitementNone .

Différences de syntaxe execentre Python 2 et Python 3

L'une des principales différences dans Python 2 est qu'il execs'agit d'une instruction et d' evalune fonction intégrée (les deux sont des fonctions intégrées dans Python 3). C'est un fait bien connu que la syntaxe officielle de execPython 2 est exec code [in globals[, locals]].

Contrairement à la majorité des Python 2 à 3 portage des guides semblent suggérer , la execdéclaration CPython 2 peut également être utilisé avec la syntaxe qui ressemble exactement comme l' execinvocation de fonction en Python 3. La raison en est que Python 0.9.9 avait l' exec(code, globals, locals)intégré en fonction! Et cette fonction intégrée a été remplacée par une execdéclaration quelque part avant la sortie de Python 1.0 .

Puisqu'il était souhaitable de ne pas rompre la rétrocompatibilité avec Python 0.9.9, Guido van Rossum a ajouté un hack de compatibilité en 1993 : si le codeétait un tuple de longueur 2 ou 3, globalset localsn'était pas passé dans l' execinstruction autrement, le codeserait interprété comme si les 2e et 3e éléments du tuple étaient respectivement le globalset locals. Le piratage de compatibilité n'était pas mentionné même dans la documentation Python 1.4 (la première version disponible en ligne) ; et donc n'était pas connu de nombreux auteurs des guides et outils de portage, jusqu'à ce qu'il soit à nouveau documenté 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. Le formulaire exec(expr, globals)est équivalent à exec expr in globals, tandis que le formulaire exec(expr, globals, locals)est équivalent à exec expr in globals, locals. La forme tuple de execfournit la compatibilité avec Python 3, où se exectrouve une fonction plutôt qu'une instruction.

Oui, dans CPython 2.7, elle est désignée comme une option de compatibilité descendante (pourquoi confondre les gens avec le fait qu'il existe une option de compatibilité descendante), alors qu'elle était en fait là pour la compatibilité descendante depuis deux décennies .

Ainsi, while execest une instruction en 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 un comportement identique dans toutes les versions Python largement diffusées; et fonctionne également dans Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) et IronPython 2.6.1 (bravo à eux en suivant de près le comportement non documenté de CPython).

Ce que vous ne pouvez pas faire dans Pythons 1.0 - 2.7 avec son hack de compatibilité, c'est de stocker la valeur de retour de execdans 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

(qui ne serait pas utile non plus dans Python 3, comme exectoujours retourne None), ou passez 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 modèle que quelqu'un aurait pu utiliser, quoique peu probable;

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 forplutôt une boucle!).

Antti Haapala
la source
Était-ce [i for i in globals().values() if hasattr(i, '__call__')][0]une déclaration ou une expression? Si c'était une expression, pourquoi ne puis-je pas l'utiliser avec @comme décorateur?
Mario
c'est une expression. 42est aussi une expression, et vous ne pouvez pas l'utiliser avec @comme décorateur.
Antti Haapala
La syntaxe du décorateur est decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; c'est-à-dire que vous ne pouvez pas utiliser d'expressions arbitraires comme décorateurs, UNIQUEMENT un identifiant (peut-être en pointillé), suivi d'arguments d'appel facultatifs.
Antti Haapala
1
Tout ce qui peut être placé du côté droit d'une affectation et toujours compiler n'est pas une expression. Par exemple, a = b = cest une déclaration parfaitement valide, tout comme son côté droit b = c- qui n'est pas une expression.
Tom
194
  1. execn'est pas une expression: une instruction en Python 2.x et une fonction en Python 3.x. Il compile et évalue immédiatement une instruction ou un ensemble d'instructions contenues 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.
  2. evalest une fonction intégrée (et non une instruction), qui évalue une expression et renvoie la valeur produite par l'expression. 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.
  3. compileest une version de niveau inférieur de execet eval. Il n'exécute ni n'évalue vos instructions ou expressions, mais renvoie un objet de code qui peut le faire. Les modes sont les suivants:

    1. compile(string, '', 'eval')renvoie l'objet de code qui aurait été exécuté si vous l'aviez fait eval(string). Notez que vous ne pouvez pas utiliser d'instructions dans ce mode; seule une expression (unique) est valide.
    2. compile(string, '', 'exec')renvoie l'objet de code qui aurait été exécuté si vous l'aviez fait exec(string). Vous pouvez utiliser n'importe quel nombre de déclarations ici.
    3. compile(string, '', 'single')est comme le execmode, mais il ignorera tout sauf la première instruction. Notez qu'une instruction if/ elseavec ses résultats est considérée comme une instruction unique.
Max Shawabkeh
la source
40
En Python 3, c'est exec()maintenant en fait une fonction.
Tim Pietzcker
2
Puisque (comme vous le faites remarquer), execest une déclaration dans la version que vous visiez, il est trompeur d'inclure ces parens, et si vous essayez d'utiliser le in globals, locals, également bogué.
Mike Graham
2
@MikeGraham exec prend en charge les parenthèses et fonctionne comme l'invocation dans Python 2 .
Antti Haapala
2
@AnttiHaapala dans la mesure où l'affectation «prend en charge les parenthèses» parce que vous pouvez le faire x = (y), cela pourrait être vrai. Une autre instruction transformée en fonction est print; comparer le résultat de print(1, 2, 3)en python 2 et 3.
habnabit
1
@habnabit pas comme ça. Veuillez lire le bas de ma réponse ici et soyez surpris.
Antti Haapala
50

exec est pour la déclaration et ne renvoie rien. eval est pour l'expression et renvoie la valeur de l'expression.

l'expression signifie «quelque chose» tandis que l'instruction signifie «faire quelque chose».

Wu Li
la source
9
Le deuxième paragraphe est une telle simplification qu'il devient presque un mensonge, une expression peut très bien faire quelque chose si elle inclut un appel de fonction.
Antti Haapala