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

He estado estudiando la evaluación dinámica del código Python, y me he topado con las funciones eval() y compile() y la statement exec .

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

La respuesta corta, o TL; DR

Básicamente, eval se usa para evaluar una única expresión de Python generada dinámicamente, y exec se usa para ejecutar código de Python generado dinámicamente solo por sus efectos secundarios.

eval y exec tienen estas dos diferencias:

  1. eval acepta solo una expresión , exec puede tomar un bloque de código que tenga sentencias de Python: bucles, try: except: class y función / método que define las incias, etc.

    Una expresión en Python es lo que puede tener como valor en una asignación de 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 exec ignora el valor de retorno de su código, y siempre devuelve None (en Python 2 es una statement y no se puede usar como una expresión, por lo que realmente no devuelve nada).

En las versiones 1.0 a 2.7, exec era una statement, porque CPython necesitaba producir un tipo de objeto de código diferente para las funciones que usaban exec para sus efectos secundarios dentro de la función.

En Python 3, exec es una función; Su uso no tiene efecto en el código de bytes comstackdo 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 "", line 1, in  File "", line 1 a = 47 ^ SyntaxError: invalid syntax 

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

 >>> eval(compile('42', '', 'exec')) # code returns None >>> eval(compile('42', '', 'eval')) # code returns 42 42 >>> exec(compile('42', '', 'eval')) # code returns 42, >>> # but ignored by exec 

En el modo 'eval' (y, por tanto, con la función eval si se pasa una cadena), la compile genera 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)', '', 'eval') Traceback (most recent call last): File "", line 1, in  File "", line 1 for i in range(3): print(i) ^ SyntaxError: invalid syntax 

En realidad, la statement “eval acepta solo una expresión única” se aplica solo cuando se pasa una cadena (que contiene el código fuente de Python) a eval . Luego se comstack internamente a bytecode usando compile(source, '', 'eval') Aquí es donde realmente viene la diferencia.

Si un objeto de code (que contiene el bytecode de Python) se pasa a exec o eval , se comportan de manera idéntica , excepto por el hecho de que exec ignora el valor de retorno, y siempre devuelve None . Por lo tanto, es posible usar eval para ejecutar algo que tenga sentencias, si simplemente compile d en el bytecode antes en lugar de pasarlo como una cadena:

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

Funciona sin problemas, a pesar de que el código comstackdo contiene declaraciones. Todavía devuelve None , porque ese es el valor de retorno del objeto de código devuelto desde la compile .

En el modo 'eval' (y, por tanto, con la función eval si se pasa una cadena), la compile genera 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)', ''. 'eval') Traceback (most recent call last): File "", line 1, in  File "", 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 función exec (que era una statement en Python 2 ) se usa para ejecutar una instrucción o progtwig 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 función eval 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 

exec y eval aceptan que el progtwig / expresión se ejecute como un objeto str , unicode o bytes que contengan código fuente, o como un objeto de code que contenga el code bytes Python.

Si una str / código / bytes contiene código fuente se pasó a exec , se comporta de manera equivalente a:

 exec(compile(source, '', 'exec')) 

y eval se comporta de manera similar equivalente a:

 eval(compile(source, '', 'eval')) 

Dado que todas las expresiones se pueden usar como declaraciones en Python (estos se denominan nodos Expr en la gramática abstracta de Python; lo contrario no es cierto), siempre puede usar exec si no necesita el valor de retorno. Es decir, puede usar eval('my_func(42)') o exec('my_func(42)') , la diferencia es que eval devuelve el valor devuelto por my_func , y exec descarta:

 >>> 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, solo exec acepta código fuente que contiene sentencias, como def , for , while , import o class , la sentencia de asignación (también conocida a = 42 ) o progtwigs completos:

 >>> 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 "", line 1, in  File "", line 1 for i in range(3): print(i) ^ SyntaxError: invalid syntax 

Tanto exec como eval aceptan 2 argumentos posicionales adicionales, globals y locals , que son los ámbitos de variables globales y locales que ve el código. Estos globals() predeterminados son globals() y locals() dentro del ámbito que se llama exec o eval , pero cualquier diccionario se puede usar para globals y cualquier mapping para locals (incluido el dict por supuesto). Estos se pueden usar no solo para restringir / modificar las variables que ve el código, sino que también se usan a menudo para capturar las variables que crea el código ejecutado:

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

(Si muestra el valor de la g completa, sería mucho más largo, ya que exec y eval agregan el módulo __builtins__ como __builtins__ a los globales automáticamente si falta).

En Python 2, la syntax oficial de la sentencia exec es en realidad exec code in globals, locals , como en

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

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

compile

La compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) integrada se puede usar para acelerar las invocaciones repetidas del mismo código con exec o eval comstackndo la fuente en un objeto de code antemano . El parámetro de mode controla el tipo de fragmento de código que la función de compile acepta y el tipo de bytecode que produce. Las opciones son 'eval' , 'exec' y 'single' :

  • 'eval' modo 'eval' 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', '', '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 únicas a módulos completos de código, y las ejecuta como si fueran declaraciones de nivel superior de módulos. El objeto de código devuelve None :

     >>> dis.dis(compile('a + b', '', '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 'exec' que acepta un código fuente que contiene una sola statement (o varias declaraciones separadas por ; ) si la última statement es una statement de expresión, el código de bytes resultante también imprime la reproducción del valor de esa expresión a la salida estándar (!) .

    Una cadena if - elif - else , un bucle con else , e try con sus bloques except , else y finally se considera una sola statement.

    Un fragmento de origen que contiene 2 declaraciones de nivel superior es un error para el 'single' , excepto que en Python 2 hay un error que a veces permite múltiples declaraciones de nivel superior en el código; sólo se comstack el primero; el rest son ignorados

    En Python 2.7.8:

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

    Y en Python 3.4.2:

     >>> exec(compile('a = 5\na = 6', '', 'single')) Traceback (most recent call last): File "", line 1, in  File "", 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 eval el código resultante.

Por lo tanto, la mayor distinción de exec y eval proviene realmente de la función de compile y sus modos.


Además de comstackr el código fuente a bytecode, compile admite la comstackción de árboles de syntax abstracta (árboles de código Python) en objetos de code ; y el código fuente en árboles de syntax abstracta ( ast.parse está escrito en Python y solo llama a 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 dinámica de código, 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.


Mientras que eval solo le permite evaluar una cadena que contiene una sola expresión, puede evaluar una statement completa, o incluso un módulo completo que se haya compile en el bytecode; es decir, con Python 2, la print es una statement y no se puede eval directamente:

 >>> eval('for i in range(3): print("Python is cool")') Traceback (most recent call last): File "", line 1, in  File "", line 1 for i in range(3): print("Python is cool") ^ SyntaxError: invalid syntax 

compile con 'exec' modo 'exec' en un objeto de code y podrá eval ; la función eval devolverá 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 el código fuente de eval y exec en CPython 3, esto es muy evidente; ambos llaman a PyEval_EvalCode con los mismos argumentos, la única diferencia es que exec explícitamente devuelve None .

Diferencias de syntax de exec entre Python 2 y Python 3

Una de las principales diferencias en Python 2 es que exec es una statement y eval es una función incorporada (ambas son funciones incorporadas en Python 3). Es un hecho bien conocido que la syntax oficial de exec en Python 2 es el exec code [in globals[, locals]] .

A diferencia de la mayoría de las guías de conversión de Python 2 a 3 que parecen sugerir , la statement exec en CPython 2 también se puede usar con una syntax que se parece exactamente a la invocación de la función exec en Python 3. La razón es que Python 0.9.9 tenía el ¡Función incorporada exec(code, globals, locals) ! Y esa función incorporada se reemplazó con la statement exec algún lugar antes del lanzamiento de Python 1.0 .

Como era deseable no interrumpir la compatibilidad con Python 0.9.9, Guido van Rossum agregó un truco de compatibilidad en 1993 : si el code era una tupla de longitud 2 o 3, y los globals y locals no se pasaron a la statement exec otra manera, el code se interpretaría como si el segundo y tercer elemento de la tupla fueran los globals y los locals respectivamente. El truco de compatibilidad no se mencionó ni siquiera en la documentación de Python 1.4 (la primera versión disponible en línea) ; y por lo tanto, muchos escritores de las guías y herramientas de adaptación no lo sabían, hasta que fue documentado nuevamente 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. La forma exec(expr, globals) es equivalente a exec expr in globals , mientras que la forma exec(expr, globals, locals) es equivalente a exec expr in globals, locals . La forma de tupla de exec proporciona compatibilidad con Python 3, donde exec es una función en lugar de una statement.

Sí, en CPython 2.7, se lo denomina fácilmente como una opción de compatibilidad hacia adelante (por qué confundir a las personas con la opción de compatibilidad hacia atrás), cuando en realidad estuvo allí por compatibilidad hacia atrás durante dos décadas .

Así, mientras que exec es una statement 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 también funciona en Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) y IronPython 2.6.1 (felicitaciones a ellos siguiendo de cerca el comportamiento no documentado 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 exec en 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 "", line 1 a = exec('print(42)') ^ SyntaxError: invalid syntax 

(lo que tampoco sería útil en Python 3, ya que exec siempre devuelve None ), o pasa una referencia a exec :

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

Cuál es un patrón que alguien podría haber usado, aunque improbable;

O utilízalo en una lista de comprensión:

 >>> [exec(i) for i in ['print(42)', 'print(foo)'] File "", line 1 [exec(i) for i in ['print(42)', 'print(foo)'] ^ SyntaxError: invalid syntax 

que es un abuso de las listas de comprensión (use un bucle for lugar!).

  1. exec no es una expresión: una statement en Python 2.x, y una función en Python 3.x. Comstack e inmediatamente evalúa una statement o conjunto de instrucciones 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. eval es una función incorporada ( no una statement), 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. compile es una versión de nivel inferior de exec y 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 usted hubiera hecho eval(string) . Tenga en cuenta que no puede usar sentencias 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 usted hubiera ejecutado exec(string) . Puede utilizar cualquier número de declaraciones aquí.
    3. compile(string, '', 'single') es como el modo exec , pero ignorará todo excepto la primera instrucción. Tenga en cuenta que una statement if / else con sus resultados se considera una sola statement.

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

expresión significa “algo” mientras que statement significa “hacer algo”.