13.6 Interpreter im Interpreter 

In bestimmten Fällen ist es nützlich, vom Benutzer eingegebenen oder anderweitig zur Laufzeit geladenen Python-Code aus einem Python-Programm heraus ausführen zu können. Stellen Sie sich einmal vor, Sie wollten ein Programm schreiben, das Wertetabellen für beliebige Funktionen mit einem ganzzahligen Parameter darstellen kann. Für ein solches Programm muss der Benutzer die Funktion festlegen können. Anstatt dafür eine eigene Sprache zu definieren und einen eigenen Parser und Compiler zu schreiben, bietet es sich an, Funktionsdefinitionen in Python-Syntax zu erlauben.
Mithilfe des exec-Statements können wir genau dies erreichen. Pythons exec-Anweisung erwartet einen String als Parameter, der den auszuführenden Code enthält. Alternativ kann auch ein geöffnetes Datei-Objekt an exec übergeben werden.
Um beispielsweise eine vom Benutzer eingegebene Funktion für die Ausgabe einer kleinen Wertetabelle zu benutzen, dient der folgende Code-Schnipsel:
print "Definieren Sie eine Funktion f mit einem Parameter:" definition = raw_input() exec definition for i in range(5): print "f(%d) = %f" % (i, f(i))
Ein Programmlauf könnte dann wie folgt aussehen:
Definieren Sie eine Funktion f mit einem Parameter: def f(x): return x*x f(0) = 0.000000 f(1) = 1.000000 f(2) = 4.000000 f(3) = 9.000000 f(4) = 16.000000
Wie Sie sehen, ist die Funktion f, die von dem Benutzer definiert wurde, nach dem Ausführen von exec im lokalen Namensraum unseres Programms verfügbar, denn wir können sie ganz normal aufrufen. Ebenso kann der Benutzer neue Variablen anlegen oder den Wert bereits bestehender Variablen auslesen, was allerdings ein Sicherheitsrisiko darstellt.
Um die Sicherheit zu erhöhen, kann man den mit exec ausgeführten Code in einem eigenen Namensraum »einsperren«. Alle neuen Variablen, Klassen und Funktionen werden in diesem gesonderten Namensraum abgelegt. Außerdem sind dem exec-Code nur noch die Variablen zugänglich, die in seinem Namensraum vorhanden sind. Ein Namensraum ist ein einfaches Dictionary, das den Referenznamen ihre Werte zuordnet. Um einem exec-Statement einen eigenen Namensraum zu geben, stellt man das Dictionary zusammen mit einem in hinten an:
>>> kontext = {"pi" : 3.1459} >>> exec "print pi" in kontext 3.1459
Alle Referenzen, die innerhalb des exec-Codes definiert wurden, sind anschließend auch in dem übergebenen Kontext definiert. Damit können wir unser Einstiegsbeispiel gegen ungewollte Seiteneffekte absichern. Den Wert der Kreiszahl π wollen wir dem Benutzer auch für seine Funktionen zugänglich machen:
print "Definieren Sie eine Funktion f mit einem Parameter:" definition = raw_input() kontext = {"pi" : 3.1459} exec definition in kontext for i in range(5): print "f(%d) = %f" % (i, kontext['f'](i))
Ein Beispiellauf, in dem der Benutzer eine Funktion für die Berechnung der Kreisfläche anhand des Kreisradius eingibt, sähe dann so aus:
Definieren Sie eine Funktion f mit einem Parameter: def f(r): return pi * r*r f(0) = 0.000000 f(1) = 3.145900 f(2) = 12.583600 f(3) = 28.313100 f(4) = 50.334400
Ausdrücke auswerten mit eval
Während mit exec beliebiger Python-Code ausgeführt werden kann, dient die Built-in Function eval dazu, Python-Ausdrücke auszuwerten und das Ergebnis zurückzugeben:
>>> eval("5 * 4") 20
Auch der von eval ausgewertete Ausdruck hat standardmäßig Zugriff auf alle Variablen des aktuellen Kontexts. Durch die beiden zusätzlichen Parameter globals und locals kann ein benutzerdefinierter Kontext festgelegt werden.
>>> x = 10 >>> eval("5 * x") 50 >>> eval("5 * x", {}) Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> eval("5 * x", {}) File "<string>", line 1, in <module> NameError: name 'x' is not defined
Beim ersten Aufruf von eval konnten wir auf die globale Variable x zugreifen, weil der Kontext einfach kopiert wurde. Der zweite Aufruf hingegen bekam ein leeres Dictionary als Kontext übergeben, weshalb der versuchte Zugriff auf x mit einer Exception quittiert wurde.
Die vollständige Schnittstelle von eval sieht folgendermaßen aus:
eval(expression[, globals[, locals]])