Ausführen von Python-Code in einem String enthalten
Frage
Ich schreibe ein Spiel-Engine mit pygame und box2d, und im Charakter-Builder, ich möchte den Code schreiben können, die auf keydown Ereignisse ausgeführt werden.
Mein Plan war, einen Texteditor, in dem Zeichen Builder hat, dass Sie ähnlich Code schreiben lassen zu:
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
Ich werde den Inhalt des Texteditors als String abrufen, und ich mag der Code in einem Verfahren, bei dieser Methode der Zeichen ausgeführt werden:
def keydown(self, key):
## Run code from text editor
Was ist der beste Weg, das zu tun?
Lösung
Sie können über die eval(string)
Methode, dies zu tun.
Definition
eval(code, globals=None, locals=None)
Der Code ist nur Standard-Python-Code - dies bedeutet, dass es muss noch richtig eingerückt werden.
Die Globals können eine benutzerdefinierte __builtins__
definiert haben, die für Sicherungszwecke nützlich sein könnte.
Beispiel
eval("print('Hello')")
Möchten hello
an die Konsole drucken. Sie können auch lokale und globale Variablen angeben, für den Code zu verwenden:
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
Sicherheitsbedenken
Seien Sie vorsichtig, wenn. Alle Benutzereingaben werden ausgeführt. Bedenken Sie:
eval("import os;os.system('sudo rm -rf /')")
Es gibt eine Reihe von Möglichkeiten, um die. Am einfachsten ist es, etwas zu tun wie:
eval("import os;...", {'os':None})
, die eine Ausnahme werfen, anstatt die Festplatte zu löschen. Während Ihr Programm Desktop ist, könnte dies ein Problem sein, wenn die Menschen Skripte neu verteilt, die ich bestimmt vorstellen wird.
Seltsames Beispiel
Hier ist ein Beispiel für die Verwendung eval
eher seltsam:
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
Was das bedeutet auf der eval Linie ist:
- Gibt das aktuelle Modul einen anderen Namen (es
contrivedExample
an das Skript wird). Der Verbraucher kanncontrivedExample.hello()
call now.) - Sie definiert
hi
alshello
zeigen - Es kombiniert das Wörterbuch mit der Liste der aktuellen Globals im Vollstreckungs Modul.
FAIL
Es stellt sich heraus (durch commen!), Die Sie tatsächlich die exec
Anweisung verwenden müssen. Big oops. Die überarbeiteten Beispiele sind wie folgt:
exec
Definition
(Das kommt mir bekannt vor!)
Exec ist eine Aussage:
exec "code" [in scope]
Wo Umfang ist ein Wörterbuch der lokalen und globalen Variablen. Wenn dies nicht angegeben ist, führt er im aktuellen Bereich.
Der Code ist nur Standard-Python-Code - das bedeutet, dass es immer noch richtig eingerückt sein muss.
exec
Beispiel
exec "print('hello')"
Möchten hello
an die Konsole drucken. Sie können auch lokale und globale Variablen angeben, für den Code zu verwenden:
eval "print('hello, '+name)" in {'name':'person-b'}
exec
Sicherheitsbedenken
Seien Sie vorsichtig, wenn. Alle Benutzereingaben werden ausgeführt. Bedenken Sie:
exec "import os;os.system('sudo rm -rf /')"
Drucken Statement
Wie auch durch commen erwähnt, print
ist eine Aussage in allen Versionen von Python vor 3,0. In 2.6 kann das Verhalten durch die Eingabe from __future__ import print_statement
geändert werden. Andernfalls Verwendung:
print "hello"
Statt:
print("hello")
Andere Tipps
Wie andere haben darauf hingewiesen, können Sie den Text in einen String und Verwendung exec "codestring"
laden kann. Wenn bereits in einer Datei enthalten sind, unter Verwendung von execfile wird vermeiden, dass es zu laden.
Eine Leistung Hinweis: Sie sollten execing den Code mehrfach, wie Parsing vermeiden und den Python-Source Kompilieren ein langsamer Prozess ist. dh. nicht haben:
def keydown(self, key):
exec user_code
können Sie verbessern das ein wenig von der Quelle in einen Code Objekt kompiliert (mit compile()
und exec das, oder besser, durch eine Funktion erstellt, die Sie um zu halten, und nur einmal bauen. Entweder muss den Benutzer schreiben „def my_handler (args ...)“oder voranstellen, es sich selbst, und so etwas wie:
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']
Dann später:
if key == K_a:
user_func(args)
Sie können mit eval()
eval oder exec. Sie sollten auf jeden Fall Python-Bibliothek Referenz vor der Programmierung lesen.