¿Cuál es la diferencia entre eval, exec y compile?

428

He estado buscando en la evaluación dinámica de código Python, y venir a través del eval()y compile()funciones, y de la execdeclaración.

¿Puede alguien explicar la diferencia entre evaly exec, y cómo compile()encajan los diferentes modos ?

andrewdotnich
fuente

Respuestas:

517

La respuesta corta, o TL; DR

Básicamente, evalse utiliza para eval luar una sola expresión Python generado dinámicamente, y execse utiliza para exec UTE generado dinámicamente código Python sólo por sus efectos secundarios.

evaly exectienen estas dos diferencias:

  1. evalsólo acepta una sola expresión , execpuede tomar un bloque de código que tiene las sentencias de Python: bucles, try: except:, classy la función / método definitions y así sucesivamente.

    Una expresión en Python es lo que pueda tener como valor en una asignación variable:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval devuelve el valor de la expresión dada, mientras que execignora el valor de retorno de su código, y siempre devuelve None(en Python 2 es una declaración y no puede usarse como una expresión, por lo que realmente no devuelve nada).

En las versiones 1.0 - 2.7, execera una declaración, porque CPython necesitaba producir un tipo diferente de objeto de código para las funciones que usaba execpara sus efectos secundarios dentro de la función.

En Python 3, execes una función; su uso no tiene efecto en el bytecode compilado de la función donde se usa.


Así básicamente:

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

El modo compilein 'exec'compila cualquier número de declaraciones en un código de bytes que implícitamente siempre devuelve None, mientras que en el 'eval'modo compila una sola expresión en código de bytes que devuelve el valor de esa expresión.

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

En el 'eval'modo (y, por lo tanto, con la evalfunción si se pasa una cadena), se compilegenera una excepción si el código fuente contiene declaraciones o cualquier otra cosa más allá de una sola expresión:

>>> 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 realidad, la declaración "eval acepta solo una sola expresión" se aplica solo cuando se pasa una cadena (que contiene el código fuente de Python ) eval. Luego se compila internamente en bytecode usando compile(source, '<string>', 'eval')Aquí es de donde realmente viene la diferencia.

Si un codeobjeto (que contiene Python código de bytes ) se pasa a la execo eval, que se comportan de forma idéntica , excepto por el hecho de que execignora el valor de retorno, todavía volver Nonesiempre. Por lo tanto, es posible evalejecutar algo que tenga sentencias, si solo compilelo ingresó en bytecode antes en lugar de pasarlo como una cadena:

>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>

funciona sin problemas, aunque el código compilado contiene sentencias. Todavía regresa None, porque ese es el valor de retorno del objeto de código devuelto compile.

En el 'eval'modo (y, por lo tanto, con la evalfunción si se pasa una cadena), se compilegenera una excepción si el código fuente contiene declaraciones o cualquier otra cosa más allá de una sola expresión:

>>> 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 respuesta más larga, también conocida como los detalles sangrientos

exec y eval

La execfunción (que era una declaración en Python 2 ) se usa para ejecutar una declaración o programa creado dinámicamente:

>>> program = '''
for i in range(3):
    print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>> 

La evalfunción hace lo mismo para una sola expresión , y devuelve el valor de la expresión:

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

execy evaltanto aceptar el programa / expresión para ser ejecutado ya sea como una str, unicodeo bytesque contiene objeto de código fuente, o como un codeobjeto que contiene Python bytecode.

Si se pasó un str/ unicode/ bytescódigo fuente que contiene exec, se comporta de manera equivalente a:

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

y evalse comporta de manera similar a:

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

Dado que todas las expresiones se pueden usar como declaraciones en Python (estos se llaman Exprnodos en la gramática abstracta de Python ; lo contrario no es cierto), siempre se puede usar execsi no necesita el valor de retorno. Es decir, puede usar cualquiera eval('my_func(42)')o exec('my_func(42)'), la diferencia es que evaldevuelve el valor devuelto por my_funcy lo execdescarta:

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

De los 2, sólo se execacepta el código fuente que contiene las declaraciones, como def, for, while, import, o class, la instrucción de asignación (aka a = 42), o programas enteros:

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

Ambos execy evalaceptan 2 argumentos posicionales adicionales - globalsy locals- que son los ámbitos de variables globales y locales que ve el código. Estos valores predeterminados para globals()y locals()dentro del alcance que llamó execo eval, pero cualquier diccionario se puede usar para globalsy mappingpara cualquier locals(incluido, dictpor supuesto). Estos pueden usarse no solo para restringir / modificar las variables que ve el código, sino que a menudo también se usan para capturar las variables que execcrea el código uted:

>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}

(Si se visualiza el valor de la totalidad g, sería mucho más tiempo, porque execy evalañadir módulo de los muebles empotrados como __builtins__a las variables globales de forma automática si no se encuentra).

En Python 2, la sintaxis oficial para la execdeclaración es en realidad exec code in globals, locals, como en

>>> exec 'global a; a, b = 123, 42' in g, l

Sin embargo, la sintaxis alternativa exec(code, globals, locals)siempre ha sido aceptada también (ver más abajo).

compile

El compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)incorporado puede usarse para acelerar invocaciones repetidas del mismo código con execo evalcompilando la fuente en un codeobjeto de antemano. El modeparámetro controla el tipo de fragmento de código que compileacepta la función y el tipo de código de bytes que produce. Las opciones son 'eval', 'exec'y 'single':

  • 'eval'el modo espera una sola expresión, y producirá un código de bytes que cuando se ejecute devolverá el valor de esa expresión :

    >>> 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'acepta cualquier tipo de construcciones de python desde expresiones individuales hasta módulos completos de código, y las ejecuta como si fueran declaraciones de nivel superior de módulo. El objeto de código devuelve 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'es una forma limitada de la 'exec'cual acepta un código fuente que contiene una sola declaración (o varias declaraciones separadas por ;) si la última declaración es una declaración de expresión, el código de bytes resultante también imprime reprel valor de esa expresión en la salida estándar (!) .

    Un if- elif- elsecadena, con un bucle else, y trycon su except, elsey finallylos bloques se considera una única instrucción.

    Un fragmento de origen que contiene 2 declaraciones de nivel superior es un error para 'single', excepto en Python 2 que hay un error que a veces permite múltiples declaraciones de nivel superior en el código; solo se compila el primero; el resto se ignora:

    En Python 2.7.8:

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

    Y 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

    Esto es muy útil para hacer shells interactivos de Python. Sin embargo, el valor de la expresión no se devuelve , incluso si evalel código resultante.

Por lo tanto, la mayor distinción de execy en evalrealidad proviene de la compilefunción y sus modos.


Además de compilar el código fuente en bytecode, compileadmite la compilación de árboles de sintaxis abstracta (analizar árboles de código Python) en codeobjetos; y código fuente en árboles de sintaxis abstractos (el ast.parseestá escrito en Python y solo llama compile(source, filename, mode, PyCF_ONLY_AST)); estos se utilizan, por ejemplo, para modificar el código fuente sobre la marcha, y también para la creación de código dinámico, ya que a menudo es más fácil manejar el código como un árbol de nodos en lugar de líneas de texto en casos complejos.


Si bien evalsolo le permite evaluar una cadena que contiene una sola expresión, puede hacer evaluna declaración completa, o incluso un módulo completo que ha sido compiled en código de bytes; es decir, con Python 2, printes una declaración y no se puede evaldirigir directamente:

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

compilecon 'exec'mode en un codeobjeto y puedes eval hacerlo ; la evalfunción volverá 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 uno mira a los evaly execcódigo fuente en CPython 3, esto es muy evidente; Ambos llaman PyEval_EvalCodecon los mismos argumentos, la única diferencia es que execregresa explícitamenteNone .

Diferencias de sintaxis execentre Python 2 y Python 3

Una de las principales diferencias en Python 2 es que execes una declaración y evales una función integrada (ambas son funciones integradas en Python 3). Es un hecho bien conocido que la sintaxis oficial de execPython 2 es exec code [in globals[, locals]].

A diferencia de la mayoría de Python 2 a 3 portar guías parecen sugerir , la execdeclaración en CPython 2 puede ser utilizado también con la sintaxis que se ve exactamente como la execinvocación de la función en Python 3. La razón es que Python 0.9.9 tuvieron la exec(code, globals, locals)incorporada ¡en función! Y esa función incorporada fue reemplazada por una execdeclaración en algún lugar antes del lanzamiento de Python 1.0 .

Ya que era deseable para no romper la compatibilidad hacia atrás con Python 0.9.9, Guido van Rossum añadió un corte compatibilidad en 1993 : si el codeera una tupla de longitud 2 o 3, y globalsy localsno se pasaron en la execdeclaración de otro modo, el codesería interpretado como si el segundo y tercer elemento de la tupla fueran el globalsy localsrespectivamente. El truco de compatibilidad no se mencionó incluso en la documentación de Python 1.4 (la primera versión disponible en línea) ; y por lo tanto, muchos escritores no conocían las guías y herramientas de portabilidad, hasta que se volvió a documentar en noviembre de 2012 :

La primera expresión también puede ser una tupla de longitud 2 o 3. En este caso, las partes opcionales deben omitirse. El formulario exec(expr, globals)es equivalente a exec expr in globals, mientras que el formulario exec(expr, globals, locals)es equivalente a exec expr in globals, locals. La forma de tupla execproporciona compatibilidad con Python 3, donde execes una función en lugar de una declaración.

Sí, en CPython 2.7 que se conoce como una opción de compatibilidad con versiones anteriores (¿por qué confundir a las personas que hay una opción de compatibilidad con versiones anteriores?), Cuando en realidad había estado allí para la compatibilidad con versiones anteriores durante dos décadas .

Por lo tanto, while execes una declaración en Python 1 y Python 2, y una función incorporada en Python 3 y Python 0.9.9,

>>> exec("print(a)", globals(), {'a': 42})
42

ha tenido un comportamiento idéntico en posiblemente todas las versiones de Python ampliamente lanzadas; y funciona en Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) y IronPython 2.6.1 también (felicitaciones a ellos siguiendo de cerca el comportamiento indocumentado de CPython).

Lo que no puede hacer en Pythons 1.0 - 2.7 con su truco de compatibilidad es almacenar el valor de retorno de execen una 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

(que tampoco sería útil en Python 3, como execsiempre regresa None), o pasar una referencia a exec:

>>> call_later(exec, 'print(42)', delay=1000)
  File "<stdin>", line 1
    call_later(exec, 'print(42)', delay=1000)
                  ^
SyntaxError: invalid syntax

Qué patrón que alguien realmente podría haber usado, aunque poco probable;

O úsalo en una lista de comprensión:

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

lo cual es abuso de las comprensiones de listas (¡use un forbucle en su lugar!).

Antti Haapala
fuente
¿Fue [i for i in globals().values() if hasattr(i, '__call__')][0]una declaración o expresión? Si fue una expresión, ¿por qué no puedo usarlo @como decorador?
Mario
Es una expresión. 42también es una expresión, y no puede usarlo @como decorador.
Antti Haapala
La sintaxis del decorador es decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; es decir, no puede usar expresiones arbitrarias como decoradores, SOLO un identificador (tal vez punteado), seguido de argumentos de llamada opcionales.
Antti Haapala
1
No hay nada que se pueda poner en el lado derecho de una tarea y aún así compilar es una expresión. Por ejemplo, a = b = ces una declaración perfectamente válida, como lo es su lado derecho b = c, que no es una expresión.
Tom
194
  1. execno es una expresión: una declaración en Python 2.x, y una función en Python 3.x. Compila e inmediatamente evalúa una declaración o conjunto de declaraciones contenidas en una cadena. Ejemplo:

    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. evales una función incorporada ( no una declaración), que evalúa una expresión y devuelve el valor que produce esa expresión. Ejemplo:

    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. compilees una versión de nivel inferior de execy eval. No ejecuta ni evalúa sus declaraciones o expresiones, pero devuelve un objeto de código que puede hacerlo. Los modos son los siguientes:

    1. compile(string, '', 'eval')devuelve el objeto de código que se habría ejecutado si lo hubiera hecho eval(string). Tenga en cuenta que no puede usar declaraciones en este modo; solo una expresión (única) es válida.
    2. compile(string, '', 'exec')devuelve el objeto de código que se habría ejecutado si lo hubiera hecho exec(string). Puede usar cualquier cantidad de declaraciones aquí.
    3. compile(string, '', 'single')es como el execmodo, pero ignorará todo excepto la primera declaración. Tenga en cuenta que una declaración if/ elsecon sus resultados se considera una declaración única.
Max Shawabkeh
fuente
40
En Python 3, exec()ahora es de hecho una función.
Tim Pietzcker
2
Dado que (como usted señala), execes una declaración en la versión a la que estaba apuntando, es engañoso incluir esos parens, y si intenta usar el in globals, locals, también tiene errores.
Mike Graham
2
@MikeGraham exec admite paréntesis y funciona como invocación en Python 2 .
Antti Haapala
2
@AnttiHaapala en la medida en que la asignación 'admite paréntesis' porque puede hacerlo x = (y), eso podría ser cierto. Otra declaración convertida en función es print; compare el resultado de print(1, 2, 3)en python 2 y 3.
habnabit
1
@habnabit no es así. Lea la parte inferior de mi respuesta aquí y sorpréndase.
Antti Haapala
50

exec es para declaración y no devuelve nada. eval es para expresión y devuelve el valor de expresión.

expresión significa "algo", mientras que la declaración significa "hacer algo".

Wu Li
fuente
99
El segundo párrafo es tal simplificación que casi se convierte en una mentira, una expresión puede hacer mucho si incluye una llamada a la función.
Antti Haapala