2012-09-04 14 views
13

Die Python-C-API-Funktion PyEval_EvalCode lässt Sie kompilierten Python-Code ausführen. Ich möchte einen Block von Python-Code so ausführen, als ob er im Rahmen einer Funktion ausgeführt würde, so dass er sein eigenes Wörterbuch lokaler Variablen hat, die den globalen Zustand nicht beeinflussen.C Python: Ausführen von Python-Code in einem Kontext

Das scheint einfach genug zu tun, da PyEval_EvalCode können Sie einen globalen und lokalen Wörterbuch bieten:

PyObject* PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)

Das Problem, das ich in laufen hat damit zu tun, wie Python Variablennamen sucht. Betrachten Sie den folgenden Code ein, dass ich mit PyEval_EvalCode ausführen:

myvar = 300 
def func(): 
    return myvar 

func() 

Dieser einfache Code wirft tatsächlich einen Fehler, weil Python ist nicht in der Lage die Variable myvar aus func zu finden. Obwohl sich myvar im äußeren Wörterbuch im lokalen Wörterbuch befindet, kopiert Python es nicht in das lokale Wörterbuch im inneren Bereich. Der Grund dafür ist wie folgt:

Immer wenn Python nach einem Variablennamen sucht, überprüft es zuerst locals, dann überprüft es globals, und schließlich prüft es builtins. Bei Modulbereich, locals und globals sind die gleichen Wörterbuchobjekt. So wird die Anweisung x = 5 im Modulbereich x im locals Wörterbuch platziert, das auch das globals Wörterbuch ist. Nun wird eine im Modulbereich definierte Funktion, die nach x suchen muss, x nicht innerhalb des Funktionsbereichs locals finden, da Python Modul-Scope-Locals nicht in Funktionsbereichs-Locals kopiert. Aber das ist normalerweise kein Problem, denn es kann x in globals finden.

x = 5 
def foo(): 
    print(x) # This works because 'x' in globals() == True 

Es ist nur mit verschachtelten Funktionen, dass Python scheint äußeren Umfang Einheimischen in inner Umfang Einheimischen zu kopieren. (Es scheint auch so faul zu tun, nur, wenn sie innerhalb des inneren Umfangs benötigt werden.)

def foo(): 
    x = 5 
    def bar(): 
     print(x) # Now 'x' in locals() == True 
    bar() 


So das Ergebnis des Ganzen ist, dass, wenn der Code bei Modul Rahmen Ausführung, müssen Sie Stellen Sie sicher, dass Ihr globales Wörterbuch und Ihr lokales Wörterbuch das SAME-Objekt sind, andernfalls können Module-Scope-Funktionen nicht auf Module-Scope-Variablen zugreifen.

Aber in meinem Fall möchte ich nicht, dass das globale Wörterbuch und das lokale Wörterbuch gleich sind. Also muss ich dem Python-Interpreter sagen, dass ich Code im Funktionsumfang ausführe. Gibt es einen Weg, dies zu tun? Ich schaute auf die PyCompileFlags sowie die zusätzlichen Argumente zu PyEval_EvalCodeEx und kann keine Möglichkeit finden, dies zu tun.

Antwort

3

Python kopiert die äußeren Bereiche nicht in lokale Bereiche; die Dokumentation für locals heißt es:

Freie Variablen werden von den Einheimischen() zurückgegeben wird, wenn es in den Funktionsblöcken aufgerufen wird, aber nicht in der Klasse Blöcke.

Hier beziehen sich "freie" Variablen auf Variablen, die von einer verschachtelten Funktion geschlossen werden. Es ist eine wichtige Unterscheidung.

Die einfachste Lösung für Ihre Situation ist nur das gleiche dict Objekt als globals und locals weitergeben müssen:

code = """ 
myvar = 300 
def func(): 
    return myvar 

func() 
""" 
d = {} 
eval(compile(code, "<str>", "exec"), d, d) 

Andernfalls Sie Ihren Code in einer Funktion wickeln können und es aus dem kompilierten Objekt extrahieren:

s = 'def outer():\n ' + '\n '.join(code.strip().split('\n')) 
exec(compile(s, '<str>', 'exec').co_consts[0], {}, {}) 
+0

@ Channel72 siehe oben. – ecatmur