hekevintran
¿Cómo ejecuto una cadena que contiene código de Python en Python?
Brian
Para declaraciones, utilice exec(string)
(Python 2/3) o exec string
(Pitón 2):
>>> mycode="print "hello world""
>>> exec(mycode)
Hello world
Cuando necesite el valor de una expresión, utilice eval(string)
:
>>> x = eval("2+2")
>>> x
4
Sin embargo, el primer paso debe ser preguntarse si realmente es necesario. Por lo general, ejecutar código debería ser la posición de ú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.
-
pero ¿qué hay del alcance del código ejecutado por ‘ejec’? esta anidado?
– jondinham
6 de marzo de 2012 a las 10:24
-
Un caso común en el que alguien quiere usar ‘ejec’ es algo como
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
etc., que luego pueden escribir comoexec ("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, limpia y seguragetattr
: solo escribegetattr(x, s) = 42
para significar lo mismo.– ShreevatsaR
19 de abril de 2012 a las 19:18
-
¿Cómo es exec más lento que el intérprete de python?
– Cris Stringfellow
6 de febrero de 2013 a las 16:36
-
@ShreevatsaR no querrás decir
setattr(x, s, 42)
? Lo intentégetattr(x, 2) = 42
y fracasó concan't assign to function call: <string>, line 1
– Tanner Semerad
11/10/2013 a las 17:44
-
@Tanner: Mmmm. si de hecho
setattr(x, s, 42)
es la sintaxis correcta. Me sorprende que haya tardado tanto en detectarse ese error. De todos modos, el punto es quegetattr
ysetattr
son una alternativa aexec
cuando todo lo que quiere es obtener un miembro arbitrario, buscado por cadena.– ShreevatsaR
11/10/2013 a las 19:01
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()
-
cambiar stdout y stderr así me pone muy nervioso. esto parece que podría causar grandes problemas de seguridad. ¿Hay alguna manera de evitar eso?
– Narcolapsor
23 de mayo de 2011 a las 13:33
-
@Narcolapser deberías estar más preocupado por usar
exec
en absoluto (a menos que usted saber la cadena de código proviene de una fuente confiable).– bruno destuilliers
2 dic 2019 a las 17:00
alan
eval
y exec
son la solución correcta, y se pueden utilizar en un más seguro conducta.
Como se discutió en manual de referencia de python y claramente explicado en esta tutorial, el eval
y exec
Las funciones 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.
-
No es posible hacer eval seguro: Eval es realmente peligroso. Si toma el código de mí y lo evalúa, puedo fallar su programa de Python. Juego terminado.
–Ned Batchelder
18 de febrero de 2013 a las 4:20
-
@v.oddou Estaba respondiendo a la declaración de alan, “eval y exec … se pueden usar de manera segura”. esto es falso Si alguien dijera, “bash se puede usar de manera segura”, eso también sería falso. Bash es peligroso. Es un peligro necesario, pero peligroso al fin y al cabo. Afirmar que eval se puede hacer seguro es simplemente incorrecto.
–Ned Batchelder
13 de enero de 2015 a las 15:10
-
@NedBatchelder sí, de hecho. y el enlace al que apuntas es un buen material. con el poder viene la responsabilidad, por lo que el punto es simplemente ser consciente del poder potencial de eval. y si decidimos que poder=peligro.
– v.oddou
14 de enero de 2015 a las 2:08
-
@NedBatchelder Muchas piezas de código escritas en Python también pueden ser peligrosas, pero ¿por qué supones eso?
eval
oexec
están destinados a ser utilizados comoexec(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 contieneexec
no es más peligroso que un programa inseguro haciendo el daño por sí mismo comoexec
no otorga ningún privilegio nuevo al programa.– Tomás Baruchel
16 de noviembre de 2016 a las 10:46
-
@ThomasBaruchel nuevamente, mi punto es contrarrestar la noción de que eval o exec pueden ser seguros. En particular, esta respuesta afirma que controlar los globales y los locales hará posible usarlos de manera segura. Eso es falso. Cada vez que usa exec y eval, debe saber con precisión qué código se está ejecutando. Si no lo hace, entonces está expuesto a operaciones peligrosas.
–Ned Batchelder
16 de noviembre de 2016 a las 23:10
hermanos
Recuerda que a partir de la versión 3 exec
es una función!
así que siempre usa exec(mystring)
en lugar de exec mystring
.
Caridorc
Evitar exec
y eval
Utilizando exec
y eval
en Python está muy mal visto.
hay mejores alternativas
De la respuesta superior (énfasis mío):
Para declaraciones, utilice
exec
.Cuando necesite el valor de una expresión, utilice
eval
.Sin embargo, el primer paso debe ser preguntarse si realmente es necesario. Ejecutar código generalmente debe ser la posición de último recurso: Es lento, feo y peligroso si puede contener código ingresado por el usuario. Siempre debes mirar alternativas primero, como funciones de orden superiorpara ver si estos pueden satisfacer mejor sus necesidades.
¿De Alternativas a exec/eval?
establecer y obtener valores de variables con los nombres en cadenas
[while
eval
] funcionaría, generalmente no se recomienda usar nombres de variables que tengan un significado para el programa en sí.En su lugar, mejor usa un dict.
no es idiomatico
Desde http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (énfasis mío)
Python no es PHP
No intente eludir los modismos 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 te da la herramienta
exec
no significa que debas usar esa herramienta.
Es peligroso
Desde http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (énfasis mío)
¡Entonces eval no es seguro, incluso si elimina todos los elementos globales y los elementos integrados!
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 fuera de la lista, puede atacar el sistema.
Entonces, ¿se puede hacer eval de forma segura? Difícil de decir. En este punto, mi mejor suposición 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é seguro. Quizás…
Es difícil de leer y entender.
Desde http://stupidpythonideas.blogspot.it/2013/05/por-que-evalexec-es-malo.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á pasando, 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. Por lo tanto, si está trabajando en un equipo, publicando software de código abierto o pidiendo ayuda en algún lugar como StackOverflow, está dificultando que otras personas lo ayuden. Y si hay alguna posibilidad de que vaya a depurar o expandir este código dentro de 6 meses, lo está haciendo más difícil para usted directamente.
-
“Mi mejor suposición es que no puede hacer ningún daño si no puede usar guiones bajos dobles”: podría construir una cadena que contenga guiones bajos dobles y luego llamar a eval esa cadena.
-Stanley Bak
11/10/2016 a las 16:11
-
buen consejo, a menos que esté escribiendo un generador de código, o un ejecutor de trabajos o algo similar… que la mayoría de la gente aquí probablemente esté haciendo.
– Erik Aronesty
3 mayo 2019 a las 12:59
-
Tengo que importar una ruta relativa desde un archivo de configuración (
cfg.yaml
):reldir : ../my/dir/
yreldir = cfg[reldir]
. Sin embargo, como este código python se ejecutará tanto en Windows como en Linux, necesito que se ajuste a los diferentes divisores de ruta de los sistemas operativos; cualquiera\\
o/
. entonces usoreldir : os.path.join('..','my','dir')
en el archivo de configuración. Pero esto sólo resulta enreldir
siendo esta cadena literal, no siendo evaluada, por lo que no puedo abrir un archivo enreldir
. ¿Tienes una sugerencia?– María. pags.
11 de marzo de 2020 a las 14:18
-
Uhm, no. Estos son altamente pitónicos. ¿Qué crees que es un REPL?
– Resplandor
17 oct 2020 a las 20:30
Jérôme Verstrynge
eval()
es solo para expresiones, mientras que eval('x+1')
obras, eval('x=1')
no funcionará por ejemplo. En ese caso, es mejor usar exec
o incluso mejor: intenta encontrar una mejor solución 🙂
-
“Mi mejor suposición es que no puede hacer ningún daño si no puede usar guiones bajos dobles”: podría construir una cadena que contenga guiones bajos dobles y luego llamar a eval esa cadena.
-Stanley Bak
11/10/2016 a las 16:11
-
buen consejo, a menos que esté escribiendo un generador de código, o un ejecutor de trabajos o algo similar… que la mayoría de la gente aquí probablemente esté haciendo.
– Erik Aronesty
3 mayo 2019 a las 12:59
-
Tengo que importar una ruta relativa desde un archivo de configuración (
cfg.yaml
):reldir : ../my/dir/
yreldir = cfg[reldir]
. Sin embargo, como este código python se ejecutará tanto en Windows como en Linux, necesito que se ajuste a los diferentes divisores de ruta de los sistemas operativos; cualquiera\\
o/
. entonces usoreldir : os.path.join('..','my','dir')
en el archivo de configuración. Pero esto sólo resulta enreldir
siendo esta cadena literal, no siendo evaluada, por lo que no puedo abrir un archivo enreldir
. ¿Tienes una sugerencia?– María. pags.
11 de marzo de 2020 a las 14:18
-
Uhm, no. Estos son altamente pitónicos. ¿Qué crees que es un REPL?
– Resplandor
17 oct 2020 a las 20:30
pulpograbbus
Usted logra ejecutar el código usando exec, como con la siguiente sesión INACTIVA:
>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']
4
-
Esto no funciona en Python normal. Al menos no en Python 3.
– Tomas Ahle
20 de agosto de 2018 a las 11:22
Para ser justos, a diferencia de la otra pregunta, esta no es un duplicado. Y otros han publicado preguntas mucho más básicas que esta.
– DNS
31 de marzo de 2009 a las 16:25
La respuesta correcta, por supuesto, es casi siempre “¡no!”.
– bobince
31 de marzo de 2009 a las 16:28
@S. Lott: ¿No has leído las preguntas frecuentes recientemente? “También está perfectamente bien hacer y responder tu propia pregunta de programación, pero finge que estás en Jeopardy: exprésalo en forma de pregunta”. Esta no es una mala pregunta. +1
– Devin Jean Pierre
31 de marzo de 2009 a las 17:48
@ S. Lott: No lo haces. No es necesario. Si la pregunta aún no está en el sitio, entonces es un juego justo (según las preguntas frecuentes, como ya se señaló). Simplemente responda todas las preguntas como si el OP necesitara ayuda. Puede que no, pero el próximo tipo que lea su pregunta probablemente lo hará. Sólo mis 2 centavos.
– Bill el lagarto
2 de abril de 2009 a las 18:13
A todos los que dicen que no lo hagan: tengo un caso en el que absolutamente necesito hacerlo. Se trata de procesamiento distribuido.
– sudo
1 de agosto de 2016 a las 8:23