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?

È stato utile?

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 è:

  1. Dà al modulo corrente un altro nome (diventa contrivedExample allo script). Il consumatore può chiamare contrivedExample.hello () ora.)
  2. Definisce hi come puntato a hello
  3. 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top