¿Cómo ejecuto una cadena que contiene código Python en Python?
¿Cómo ejecuto una cadena que contiene código Python en Python?
Respuestas:
Para las declaraciones, use exec(string)
(Python 2/3) o exec string
(Python 2):
>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world
Cuando necesite el valor de una expresión, use eval(string)
:
>>> x = eval("2+2")
>>> x
4
Sin embargo, el primer paso debería ser preguntarse si realmente lo necesita. El código de ejecución generalmente debe ser el último recurso: es lento, feo y peligroso si puede contener código ingresado por el usuario. Siempre debe buscar alternativas primero, como funciones de orden superior, para ver si pueden satisfacer mejor sus necesidades.
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
, etc., que luego pueden escribir como exec ("x.%s = 42" % s)
. Para este caso común (donde solo necesita acceder al atributo de un objeto que está almacenado en una cadena), existe una función mucho más rápida, más limpia y más segura getattr
: simplemente escriba getattr(x, s) = 42
para decir lo mismo.
setattr(x, s, 42)
? Lo intenté getattr(x, 2) = 42
y falló concan't assign to function call: <string>, line 1
setattr(x, s, 42)
es la sintaxis correcta. Sorprendido, tardó tanto tiempo en detectar ese error. De todos modos, el punto es eso getattr
y setattr
son una alternativa a exec
cuando todo lo que quieres es obtener un miembro arbitrario, buscado por cadena.
En el ejemplo, una cadena se ejecuta como código utilizando la función exec.
import sys
import StringIO
# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()
code = """
def f(x):
x = x + 1
return x
print 'This is my output.'
"""
# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr
exec code
# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print f(4)
s = codeErr.getvalue()
print "error:\n%s\n" % s
s = codeOut.getvalue()
print "output:\n%s" % s
codeOut.close()
codeErr.close()
exec
(a menos que sepa que la cadena de código proviene de una fuente confiable).
eval
y exec
son la solución correcta, y pueden usarse de una manera más segura .
Como se discutió en el manual de referencia de Python y se explicó claramente en este tutorial, las funciones eval
y exec
toman dos parámetros adicionales que permiten al usuario especificar qué funciones y variables globales y locales están disponibles.
Por ejemplo:
public_variable = 10
private_variable = 2
def public_function():
return "public information"
def private_function():
return "super sensitive information"
# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len
>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12
>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined
>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters
>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined
En esencia, está definiendo el espacio de nombres en el que se ejecutará el código.
eval
o exec
está destinado a ser utilizado exec(input("Type what you want"))
? Hay muchos casos en los que un programa puede escribir un procedimiento o una función como resultado de un cálculo; Las funciones resultantes serán tan seguras y rápidas (una vez evaluadas) como cualquier otra parte de un programa bueno y bien escrito. Un programa inseguro que contiene exec
no es más peligroso que un programa inseguro que hace el daño por sí mismo, ya que exec
no le da ningún nuevo privilegio al programa.
exec
yeval
exec
y eval
en Python está muy mal visto.De la respuesta superior (énfasis mío):
Para declaraciones, uso
exec
.Cuando necesite el valor de una expresión, use
eval
.Sin embargo, el primer paso debería ser preguntarse si realmente lo necesita. El código de ejecución generalmente debe ser el último recurso : es lento, feo y peligroso si puede contener código ingresado por el usuario. Siempre debe buscar alternativas primero, como funciones de orden superior , para ver si pueden satisfacer mejor sus necesidades.
¿De alternativas a exec / eval?
establecer y obtener valores de variables con los nombres en cadenas
[mientras
eval
] funcionaría, generalmente no se recomienda usar nombres de variables que tengan un significado para el programa en sí.En cambio, mejor use un dict.
De http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (el énfasis es mío)
Python no es PHP
No intentes eludir las expresiones idiomáticas de Python porque algún otro idioma lo hace de manera diferente. Los espacios de nombres están en Python por una razón y solo porque le brinde la herramienta
exec
no significa que deba usar esa herramienta.
De http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (énfasis mío)
¡Eva no es seguro, incluso si elimina todos los globals y los builtins!
El problema con todos estos intentos de proteger eval () es que son listas negras . Eliminan explícitamente cosas que podrían ser peligrosas. Esa es una batalla perdida porque si solo queda un elemento de la lista, puedes atacar el sistema .
Entonces, ¿se puede evaluar la seguridad? Difícil de decir. En este punto, mi mejor conjetura es que no puede hacer ningún daño si no puede usar guiones bajos dobles, por lo que tal vez si excluye cualquier cadena con guiones bajos dobles está a salvo. Tal vez...
De http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (énfasis mío):
Primero,
exec
hace que sea más difícil para los seres humanos leer su código . Para averiguar qué está sucediendo, no solo tengo que leer su código, tengo que leer su código, averiguar qué cadena va a generar y luego leer ese código virtual. Entonces, si está trabajando en un equipo, o está publicando software de código abierto, o solicita ayuda en algún lugar como StackOverflow, está haciendo que sea más difícil para otras personas ayudarlo. Y si hay alguna posibilidad de que vaya a depurar o expandir este código dentro de 6 meses, lo está dificultando directamente.
cfg.yaml
): reldir : ../my/dir/
y reldir = cfg[reldir]
. Sin embargo, como este código de Python se ejecutará tanto en Windows como en Linux, necesito que esto se ajuste a los diferentes divisores de ruta de los sistemas operativos; ya sea \\
o /
. Entonces lo uso reldir : os.path.join('..','my','dir')
en el archivo de configuración. Pero esto solo resulta reldir
ser esta cadena literal, no se evalúa, por lo que no puedo abrir un archivo reldir
. ¿Tienes una sugerencia?
Logra ejecutar código usando exec, como con la siguiente sesión IDLE:
>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']
4
Como los otros mencionaron, es "ejecutivo".
pero, en caso de que su código contenga variables, puede usar "global" para acceder a él, también para evitar que el compilador genere el siguiente error:
NameError: el nombre 'p_variable' no está definido
exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Vale la pena mencionar que ese exec
hermano existe también llamado execfile
si desea llamar a un archivo python. Eso a veces es bueno si está trabajando en un paquete de terceros que tiene IDE terribles incluidos y desea codificar fuera de su paquete.
Ejemplo:
execfile('/path/to/source.py)'
o:
exec(open("/path/to/source.py").read())
Intenté algunas cosas, pero lo único que funcionó fue lo siguiente:
temp_dict = {}
exec("temp_dict['val'] = 10")
print(temp_dict['val'])
salida:
10
Ok .. Sé que esto no es exactamente una respuesta, pero posiblemente una nota para la gente que mira esto como yo. Quería ejecutar código específico para diferentes usuarios / clientes pero también quería evitar el exec / eval. Inicialmente busqué almacenar el código en una base de datos para cada usuario y hacer lo anterior.
Terminé creando los archivos en el sistema de archivos dentro de una carpeta 'customer_filters' y usando el módulo 'imp', si no se aplicó ningún filtro para ese cliente, simplemente continuó
import imp
def get_customer_module(customerName='default', name='filter'):
lm = None
try:
module_name = customerName+"_"+name;
m = imp.find_module(module_name, ['customer_filters'])
lm = imp.load_module(module_name, m[0], m[1], m[2])
except:
''
#ignore, if no module is found,
return lm
m = get_customer_module(customerName, "filter")
if m is not None:
m.apply_address_filter(myobj)
para que customerName = "jj" ejecute apply_address_filter desde el archivo customer_filters \ jj_filter.py