example python dynamic eval exec

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



exec python example (3)

He estado estudiando la evaluación dinámica del código Python, y me he topado con las funciones eval() y compile() y la declaración 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, etc., 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 declaración 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 declaración, 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 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

La compile en modo ''exec'' compila cualquier número de declaraciones en un bytecode que implícitamente siempre devuelve None , mientras que en ''eval'' modo ''eval'' compila una sola expresión en bytecode 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 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)'', ''<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 expresión" se aplica solo cuando se pasa una cadena (que contiene el código fuente de Python) a eval . Luego se compila internamente a bytecode usando compile(source, ''<string>'', ''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")'', ''<string>'', ''exec'')) Hello >>>

Funciona sin problemas, a pesar de que el código compilado 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)'', ''<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 función exec (que era una declaración en Python 2 ) se usa para ejecutar una instrucció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 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 programa / 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 el código fuente se pasó a exec , se comporta de manera equivalente a:

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

y eval se comporta de manera similar equivalente a:

eval(compile(source, ''<string>'', ''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 programas 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 "<stdin>", line 1, in <module> File "<string>", 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). 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 sintaxis 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 sintaxis 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) incorporada puede usarse para acelerar invocaciones repetidas del mismo código con exec o eval compilando la fuente en un objeto de code antemano . El parámetro de mode controla el tipo de fragmento de código que acepta la función de compile 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'', ''<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ódulos. 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 ''exec'' que acepta un código fuente que contiene una sola declaración (o varias declaraciones separadas por ; ) si la última instrucción es una declaración 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 declaración.

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

    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 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 compilar el código fuente a bytecode, compile soporta compilar árboles de sintaxis abstracta (árboles de código Python) en objetos de code ; y el código fuente en árboles de sintaxis 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 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 eval solo le permite evaluar una cadena que contiene una sola expresión, puede evaluar una declaración completa, o incluso un módulo completo que se haya compile en el bytecode; es decir, con Python 2, la print es una declaración y no se puede eval 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

compile con ''exec'' modo ''exec'' en un objeto de code y puedes 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 sintaxis de exec entre Python 2 y Python 3

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

A diferencia de la mayoría de las guides porting de Python 2 a 3 que seem sugerir , la declaración exec en CPython 2 también se puede usar con una sintaxis 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 declaración 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 declaración exec contrario, 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 documented 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 declaración.

Sí, en CPython 2.7, se lo denomina fácilmente como una opción de compatibilidad hacia adelante (por qué confundir a la gente 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 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 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 "<stdin>", 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 "<stdin>", 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 poco probable;

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

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


  1. exec no 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 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 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. 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 declaración if / else con sus resultados se considera una sola declaración.

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

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