Ejecutando el código Python contenido en una cadena

Estoy escribiendo un motor de juego usando pygame y box2d, y en el creador 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 te 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 caracteres:

 def keydown(self, key): ## Run code from text editor 

¿Cuál es la mejor manera de hacer eso?

Puedes usar el método eval(string) para hacer esto.

Definición

eval(code, globals=None, locals=None)
El código es solo el código estándar de Python, lo que significa que aún debe estar correctamente sangrado.

Los globales pueden tener un __builtins__ personalizado definido, que podría ser útil por motivos de seguridad.

Ejemplo

 eval("print('Hello')") 

Imprimiría hello a la consola. También puede especificar variables locales y globales para que el código use:

 eval("print('Hello, %s'%name)", {}, {'name':'person-b'}) 

Preocupaciones de seguridad

Pero ten cuidado. Cualquier entrada de usuario será ejecutada. Considerar:

 eval("import os;os.system('sudo rm -rf /')") 

Hay varias maneras de evitar eso. Lo más fácil es hacer algo como:

 eval("import os;...", {'os':None}) 

Lo que arrojará una excepción, en lugar de borrar tu disco duro. Si bien su progtwig es de escritorio, esto podría ser un problema si las personas redistribuyen los scripts, lo cual 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:

  1. Le da al módulo actual otro nombre (se convierte en un contrivedExample de la secuencia de comandos). El consumidor puede llamar a contrivedExample.hello() ahora.)
  2. Se define hi como apuntando a hello
  3. Combinó ese diccionario con la lista de globales actuales en el módulo de ejecución.

FALLAR

Resulta (¡gracias a los comentaristas!) Que realmente necesita usar la statement exec . Ups grandes. Los ejemplos revisados ​​son los siguientes:


exec definición

(¡Esto parece familiar!) Exec es una statement:
exec "code" [in scope] donde scope es un diccionario de variables locales y globales. Si esto no se especifica, se ejecuta en el scope actual.

El código es solo el código estándar de Python, lo que significa que aún debe estar correctamente sangrado.

Ejemplo de exec

 exec "print('hello')" 

Imprimiría hello a la consola. También puede especificar variables locales y globales para que el código use:

 eval "print('hello, '+name)" in {'name':'person-b'} 

preocupaciones de seguridad del exec

Pero ten cuidado. Cualquier entrada de usuario será ejecutada. Considerar:

 exec "import os;os.system('sudo rm -rf /')" 

Imprimir Declaración

Como también lo señalaron los comentaristas, print es una statement en todas las versiones de Python anteriores a la 3.0. En 2.6, el comportamiento se puede cambiar escribiendo from __future__ import print_statement . De lo contrario, utilice:

 print "hello" 

En lugar de :

 print("hello") 

Como otros lo han señalado, puede cargar el texto en una cadena y usar el exec "codestring" . Si ya está contenido en un archivo, el uso de execfile evitará tener que cargarlo.

Una nota de rendimiento: debe evitar ejecutar el código varias veces, ya que analizar y comstackr la fuente de Python es un proceso lento. es decir. no tiene

 def keydown(self, key): exec user_code 

Puedes mejorar esto un poco comstackndo la fuente en un objeto de código (con compile() y exec que, o mejor, construyendo una función que mantengas alrededor, y solo comstackr una vez. O bien requiere que el usuario escriba “def my_handler ( args …) “, o prepéndelo 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'] 

Entonces despúes:

 if key == K_a: user_func(args) 

Puedes usar eval()

eval o exec. Definitivamente debería leer la referencia de la biblioteca de Python antes de progtwigr.