2.34. Código dinámico¶
Tres funciones incorporadas toman una cadena de código fuente de Python y la ejecutan: eval(), exec() y compile(). Juntas permiten que el código construya y ejecute más código en tiempo de ejecución – lo que ocasionalmente es exactamente la herramienta adecuada, y mucho más a menudo una fuente de errores y agujeros de seguridad.
Advertencia
Estas funciones ejecutan código Python arbitrario. Pasar entrada del usuario a eval o exec – una cadena de un archivo que el usuario puede editar, una carga útil recibida por la red, un valor escrito en una sesión del REPL – permite que esa entrada haga cualquier cosa que el script que la llama pudiera hacer, hasta el punto de borrar todos los archivos del dispositivo. Úsalas de forma deliberada, nunca dentro de código que se ejecuta automáticamente, y nunca sobre datos que no controlas.
2.34.1. eval¶
eval() ejecuta una única expresión y devuelve su valor:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
La expresión ve por defecto las variables globales y locales del llamador, por lo que name se resuelve en el segundo ejemplo. Pasar diccionarios explícitos te permite aislar la evaluación:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Incluso aislada, eval es peligrosa. Existen técnicas bien conocidas para escapar de tales aislamientos; no confíes únicamente en el truco del __builtins__ vacío para entrada no confiable.
2.34.2. exec¶
exec() ejecuta un bloque de código en lugar de una única expresión – sentencias, definiciones de funciones, bucles – y devuelve None:
exec("for i in range(3): print(i)")
Salida:
0
1
2
El bloque puede definir nombres que quedan disponibles después, con algunas salvedades sobre el ámbito local frente al global. exec dentro de una función rara vez se comporta como el programador espera; si lo necesitas, ejecútalo a nivel de módulo.
2.34.3. compile¶
compile() convierte una cadena de código fuente en un objeto de código que se puede pasar más tarde a eval() o exec(). Úsalo cuando el mismo código fuente vaya a ejecutarse muchas veces – el análisis sintáctico ocurre una sola vez, la ejecución es más rápida:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Salida:
0
1
4
9
16
El argumento intermedio es una etiqueta que aparece en los rastreos (tracebacks) si el código lanza una excepción. El tercer argumento es "eval" para una única expresión, "exec" para un bloque, o "single" para una sentencia de estilo interactivo que imprime su resultado.
2.34.4. Cuándo recurrir a estas funciones¶
Casi nunca. La mayoría de los casos de uso que imaginan los principiantes tienen alternativas más seguras:
Leer un archivo de configuración. Usa
json– datos estructurados, sin ejecución.Evaluar un valor numérico escrito por el usuario. Usa
int()/float()para analizarlo, y luego aritmética. Si el usuario realmente necesita introducir una fórmula, usa un pequeño analizador de expresiones, noeval.
Cuando realmente necesites eval / exec / compile, aísla el punto de la llamada, registra la cadena exacta que está a punto de ejecutarse y trata el código fuente como lo más sospechoso de tu programa.