¿Cómo ejecuto una cadena que contiene código Python en Python?

357

¿Cómo ejecuto una cadena que contiene código Python en Python?

Hekevintran
fuente
55
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 esto.
DNS
8
La respuesta correcta, por supuesto, es casi siempre "¡no!".
bobince
23
@S. Lott: ¿No has leído las preguntas frecuentes recientemente? "También está perfectamente bien hacer y responder su propia pregunta de programación, pero finja que está en peligro: formúlelo en forma de pregunta". Esta no es una mala pregunta. +1
Devin Jeanpierre
49
@ S.Lott: No lo haces. No necesitas hacerlo. Si la pregunta ya no está en el sitio, entonces es un juego justo (de acuerdo con las preguntas frecuentes, como ya se señaló). Simplemente responda cada pregunta como si el OP necesitara ayuda. Puede que no, pero el próximo tipo que lea su pregunta probablemente lo hará. Solo mis 2 centavos.
Bill the Lizard
1
A todos ustedes diciendo que no lo hagan: tengo un caso en el que absolutamente lo necesito. Implica procesamiento distribuido.
sudo

Respuestas:

332

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.

Brian
fuente
1
pero ¿qué hay del alcance del código ejecutado por 'exec'? ¿Está anidado?
jondinham
21
Un caso común en el que alguien quiere usar 'exec' es algo así 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) = 42para decir lo mismo.
ShreevatsaR
55
¿Cómo es exec más lento que el intérprete de Python?
Cris Stringfellow
66
@ShreevatsaR no quieres decir setattr(x, s, 42)? Lo intenté getattr(x, 2) = 42y falló concan't assign to function call: <string>, line 1
Tanner Semerad
66
@ Tanner: Hmm. Sí, de hecho, setattr(x, s, 42)es la sintaxis correcta. Sorprendido, tardó tanto tiempo en detectar ese error. De todos modos, el punto es eso getattry setattrson una alternativa a execcuando todo lo que quieres es obtener un miembro arbitrario, buscado por cadena.
ShreevatsaR
68

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()
Hekevintran
fuente
1
intercambiar stdout y stderr así me pone muy nervioso. Esto parece que podría causar grandes problemas de seguridad. ¿Hay alguna manera de evitar eso?
Narcolapser
1
@Narcolapser debería estar más preocupado por el uso de mych exec(a menos que sepa que la cadena de código proviene de una fuente confiable).
bruno desthuilliers
27

evaly execson 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 evaly exectoman 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.

alan
fuente
99
No es posible hacer que eval sea seguro: Eval realmente es peligroso . Si me quitas el código y lo evalúas, puedo desconectar tu programa Python. Juego terminado.
Ned Batchelder
3
@ v.oddou Estaba respondiendo a la declaración de alan, "eval y exec .. pueden usarse 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 a pesar de todo. Afirmar que eval puede hacerse seguro es simplemente incorrecto.
Ned Batchelder
1
@NedBatchelder sí, de hecho. y el enlace al que apunta es 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
3
@NedBatchelder Muchas piezas de código escritas en Python también pueden ser peligrosas, pero ¿por qué asume eso evalo execestá 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 execno es más peligroso que un programa inseguro que hace el daño por sí mismo, ya que execno le da ningún nuevo privilegio al programa.
Thomas Baruchel el
1
@ThomasBaruchel nuevamente, mi punto es contrarrestar la noción de que eval o exec pueden ser seguros. En particular, esta respuesta afirma que controlar a los globales y locales hará posible usarlos de manera segura. Eso es falso. Cada vez que use exec y eval, debe saber con precisión qué código se está ejecutando. Si no lo hace, entonces está abierto a operaciones peligrosas.
Ned Batchelder
21

¡Recuerde que a partir de la versión 3 execes una función!
así que siempre use en exec(mystring)lugar de exec mystring.

bheks
fuente
11

eval()es solo para expresiones, aunque eval('x+1')funciona, eval('x=1')no funcionará, por ejemplo. En ese caso, es mejor usarlo exec, o incluso mejor: intente encontrar una mejor solución :)

LGB
fuente
11

Evitar execyeval

Usar execy evalen Python está muy mal visto.

Hay mejores alternativas

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.

No es idiomático

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 execno significa que deba usar esa herramienta.

Es peligroso

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...

Es dificil de leer y entender

De http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (énfasis mío):

Primero, exechace 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.

Caridorc
fuente
"Mi mejor conjetura es que no puede hacer ningún daño si no puede usar guiones bajos dobles": puede construir una cadena que contenga guiones bajos dobles y luego llamar a evaluar esa cadena.
Stanley Bak
Un buen consejo, a menos que esté escribiendo un generador de código, un corredor de trabajos o similar ... lo que la mayoría de las personas aquí probablemente están haciendo.
Erik Aronesty
Tengo que importar una ruta relativa desde un archivo de configuración ( 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 reldirser esta cadena literal, no se evalúa, por lo que no puedo abrir un archivo reldir. ¿Tienes una sugerencia?
Marie P.
9

Logra ejecutar código usando exec, como con la siguiente sesión IDLE:

>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']

4
gus
fuente
1
Esto no funciona en python normal. Al menos no en Python 3.
Thomas Ahle
6

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)
Ghanem
fuente
5

Usa eval .

Pablo Santa Cruz
fuente
77
Eval () no ejecuta declaraciones.
RickyA
4

Vale la pena mencionar que ese exechermano existe también llamado execfilesi 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())

usuario1767754
fuente
3

Echa un vistazo a eval :

x = 1
print eval('x+1')
->2
ryeguy
fuente
10
Eval () no ejecuta declaraciones.
RickyA
1

Intenté algunas cosas, pero lo único que funcionó fue lo siguiente:

temp_dict = {}
exec("temp_dict['val'] = 10") 
print(temp_dict['val'])

salida:

10

paul-shuvo
fuente
0

La solución más lógica sería utilizar la función incorporada eval () . Otra solución es escribir esa cadena en un archivo temporal de Python y ejecutarla.

John T
fuente
0

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

Brian
fuente
1
¿Cómo manejaste la seguridad? ¿Cómo sabe que los clientes no abusarán de este privilegio?
JSBach