Ejecutar código Python contenido en una cadena
Pregunta
Estoy escribiendo un motor de juego usando pygame y box2d, y en el generador de personajes, quiero poder escribir el código que se ejecutará en eventos keydown.
Mi plan era tener un editor de texto en el generador de caracteres que le permitiera escribir código similar a:
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
Recuperaré el contenido del editor de texto como una cadena, y quiero que el código se ejecute en un método en este método de Carácter:
def keydown(self, key):
## Run code from text editor
¿Cuál es la mejor manera de hacer eso?
Solución
Puede usar el eval (string)
método para hacer esto.
Definición
eval (code, globals = None, locals = None)
El código es solo el código Python estándar, esto significa que todavía necesita una sangría adecuada.
Los globales pueden tener un __builtins__
personalizado, que podría ser útil por motivos de seguridad.
Ejemplo
eval("print('Hello')")
Imprimiría hello
en la consola. También puede especificar variables locales y globales para que el código las use:
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
Preocupaciones de seguridad
Ten cuidado, sin embargo. Cualquier entrada del usuario será ejecutada. Considere:
eval("import os;os.system('sudo rm -rf /')")
Hay varias formas de evitarlo. Lo más fácil es hacer algo como:
eval("import os;...", {'os':None})
Lo que arrojará una excepción, en lugar de borrar su disco duro. Si bien su programa es de escritorio, esto podría ser un problema si la gente redistribuye los scripts, lo que imagino que está destinado.
Ejemplo extraño
Aquí hay un ejemplo del uso de eval
bastante extraño:
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
Lo que esto hace en la línea de evaluación es:
- Le da al módulo actual otro nombre (se convierte en
ideadoEjemplo
para el script). El consumidor puede llamar acontrivedExample.hello ()
ahora.) - Define
hi
como apuntando ahello
- Combinó ese diccionario con la lista de globales actuales en el módulo en ejecución.
FAIL
Resulta (¡gracias a los comentaristas!) que realmente necesitas usar la declaración exec
. Oops grandes Los ejemplos revisados ??son los siguientes:
exec
Definición
(¡Esto parece familiar!)
Exec es una declaración:
El código es solo el código estándar de Python; esto significa que aún necesita una sangría adecuada.
exec
Ejemplo
exec "print('hello')"
Imprimiría hello
en la consola. También puede especificar variables locales y globales para que el código las use:
eval "print('hello, '+name)" in {'name':'person-b'}
exec
Preocupaciones de seguridad
Ten cuidado, sin embargo. Cualquier entrada del usuario será ejecutada. Considere:
exec "import os;os.system('sudo rm -rf /')"
Imprimir declaración
Como también señalaron los comentaristas, print
es una declaración en todas las versiones de Python anteriores a la 3.0. En 2.6, el comportamiento se puede cambiar escribiendo desde __future__ import print_statement
. De lo contrario, use:
print "hello"
En lugar de:
print("hello")
Otros consejos
Como otros han señalado, puede cargar el texto en una cadena y usar exec " codestring "
. Si ya está contenido en un archivo, usar execfile evitará tener que cargarlo.
Una nota de rendimiento: debe evitar ejecutar el código varias veces, ya que analizar y compilar la fuente de Python es un proceso lento. es decir. no tiene:
def keydown(self, key):
exec user_code
Puedes mejorar esto un poco compilando la fuente en un objeto de código (con compile ()
y ejecutando eso, o mejor, construyendo una función que mantengas alrededor, y solo compila una vez. Solicite al usuario que escriba " def my_handler (args ...) " ;, o lo anteponga usted mismo y haga algo como:
user_source = "def user_func(args):\n" + '\n'.join(" "+line for line in user_source.splitlines())
d={}
exec user_source in d
user_func = d['user_func']
Luego más tarde:
if key == K_a:
user_func(args)
Puede usar eval ()
eval o exec. Definitivamente deberías leer la referencia de la biblioteca de Python antes de programar.