web-dev-qa-db-fra.com

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

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?

392
andrewdotnich

La réponse courte, ou TL; DR

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:

  1. 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 definitions, 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)
    
  2. evalrenvoie la valeur de l'expression donnée, alors que exec ignore la valeur de retour de son code et renvoie toujours Nonedans 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 codequi 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 compiled 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

La réponse la plus longue, a.k.a les détails sanglants

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 localsy 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 executed:

>>> 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é compiled en bytecode; c'est-à-dire que, avec Python 2, print est une instruction et ne peut pas être evalled 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 .

Différences de syntaxe de exec entre Python 2 et Python 3

Une 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 execquelque 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 forme exec(expr, globals, locals) est équivalente à exec expr in globals, locals. La forme Tuple de exec 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!).

452
Antti Haapala
  1. 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.
    
  2. 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.
    
  3. 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:

    1. 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.
    2. 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.
    3. 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.
192
Max Shawabkeh

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

49
Wu Li