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:
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)
eval
devuelve el valor de la expresión dada, mientras queexec
ignora el valor de retorno de su código, y siempre devuelveNone
(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 devuelveNone
:>>> 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 conelse
, etry
con sus bloquesexcept
,else
yfinally
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 ignoradosEn 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 aexec expr in globals
, mientras que la formaexec(expr, globals, locals)
es equivalente aexec expr in globals, locals
. La forma de tupla deexec
proporciona compatibilidad con Python 3, dondeexec
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!).
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.
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.
compile
es una versión de nivel inferior deexec
yeval
. No ejecuta ni evalúa sus declaraciones o expresiones, pero devuelve un objeto de código que puede hacerlo. Los modos son los siguientes:-
compile(string, '''', ''eval'')
devuelve el objeto de código que se habría ejecutado si usted hubiera hechoeval(string)
. Tenga en cuenta que no puede usar sentencias en este modo; solo una expresión (única) es válida. -
compile(string, '''', ''exec'')
devuelve el objeto de código que se habría ejecutado si usted hubiera ejecutadoexec(string)
. Puede utilizar cualquier número de declaraciones aquí. -
compile(string, '''', ''single'')
es como el modoexec
, pero ignorará todo excepto la primera instrucción. Tenga en cuenta que una declaraciónif
/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".