Esecuzione del codice Python contenuto in una stringa
Domanda
Sto scrivendo un motore di gioco usando pygame e box2d e nel generatore di personaggi, voglio essere in grado di scrivere il codice che verrà eseguito su eventi keydown.
Il mio piano era di avere un editor di testo nel generatore di caratteri che ti permettesse di scrivere codice simile a:
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
Recupero il contenuto dell'editor di testo come stringa e voglio che il codice venga eseguito in un metodo in questo metodo di Character:
def keydown(self, key):
## Run code from text editor
Qual è il modo migliore per farlo?
Soluzione
Puoi utilizzare eval (string)
metodo per farlo.
Definizione
eval (code, globals = None, locals = None)
Il codice è solo il codice Python standard, ciò significa che deve ancora essere correttamente indentato.
I globi possono avere un __builtins__
personalizzato, che potrebbe essere utile per motivi di sicurezza.
Esempio
eval("print('Hello')")
Stampa ciao
sulla console. Puoi anche specificare variabili locali e globali che il codice può usare:
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
Preoccupazioni per la sicurezza
Stai attento, però. Qualsiasi input dell'utente verrà eseguito. Prendere in considerazione:
eval("import os;os.system('sudo rm -rf /')")
Ci sono molti modi per aggirare questo. Il più semplice è fare qualcosa del tipo:
eval("import os;...", {'os':None})
Che genererà un'eccezione, piuttosto che cancellare il tuo disco rigido. Mentre il tuo programma è desktop, questo potrebbe essere un problema se le persone ridistribuissero gli script, che immagino siano destinati.
Esempio strano
Ecco un esempio dell'uso di eval
in modo piuttosto strano:
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
Quello che fa sulla linea di valutazione è:
- Dà al modulo corrente un altro nome (diventa
contrivedExample
allo script). Il consumatore può chiamarecontrivedExample.hello ()
ora.) - Definisce
hi
come puntato ahello
- Ha combinato quel dizionario con l'elenco degli attuali globali nel modulo di esecuzione.
FAIL
Si scopre (grazie ai commentatori!) che in realtà è necessario utilizzare l'istruzione exec
. Oops grandi. Gli esempi modificati sono i seguenti:
exec
Definizione
(Sembra familiare!)
Exec è una dichiarazione:
exec " code " [nell'ambito]
Dove scope è un dizionario di variabili locali e globali. Se questo non è specificato, viene eseguito nell'ambito corrente.
Il codice è solo un codice Python standard, ciò significa che deve ancora essere correttamente indentato.
exec
Esempio
exec "print('hello')"
Stampa ciao
sulla console. Puoi anche specificare variabili locali e globali che il codice può usare:
eval "print('hello, '+name)" in {'name':'person-b'}
exec
Preoccupazioni di sicurezza
Stai attento, però. Qualsiasi input dell'utente verrà eseguito. Prendere in considerazione:
exec "import os;os.system('sudo rm -rf /')"
Stampa dichiarazione
Come notato anche dai commentatori, print
è una dichiarazione in tutte le versioni di Python precedenti alla 3.0. In 2.6, il comportamento può essere modificato digitando da __future__ import print_statement
. Altrimenti, utilizzare:
print "hello"
Invece di:
print("hello")
Altri suggerimenti
Come altri hanno sottolineato, puoi caricare il testo in una stringa e utilizzare exec " codestring "
. Se già contenuto in un file, l'utilizzo di execfile eviterà di caricarlo.
Una nota sulle prestazioni: dovresti evitare di eseguire il codice più volte, poiché l'analisi e la compilazione del sorgente python è un processo lento. vale a dire. non avere:
def keydown(self, key):
exec user_code
Puoi migliorarlo un po 'compilando il sorgente in un oggetto codice (con compile ()
ed eseguendolo, o meglio, costruendo una funzione che tieni attorno e costruisci solo una volta. O chiedi all'utente di scrivere " def my_handler (args ...) " oppure di anteporre a te stesso e fare qualcosa del tipo:
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']
Quindi:
if key == K_a:
user_func(args)
Puoi utilizzare eval ()
eval o exec. Dovresti assolutamente leggere il riferimento alla libreria Python prima di programmare.