2012-09-04 17 views
13

La funzione API C Python PyEval_EvalCode consente di eseguire codice Python compilato. Voglio eseguire un blocco di codice Python come se fosse eseguito nell'ambito di una funzione, in modo che abbia il proprio dizionario di variabili locali che non influenzano lo stato globale.C Python: esecuzione di codice Python all'interno di un contesto

Questo sembra abbastanza facile da fare, dal momento che PyEval_EvalCode consente di fornire un globale e locale dizionario:

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

Il problema che ho incontrato ha a che fare con il modo in Python guarda in alto i nomi delle variabili. Si consideri il seguente codice, che eseguo con PyEval_EvalCode:

myvar = 300 
def func(): 
    return myvar 

func() 

Questo semplice codice solleva in realtà un errore, perché Python è in grado di trovare la variabile myvar dall'interno func. Anche se myvar si trova nel dizionario locale nell'ambito esterno, Python non lo copia nel dizionario locale nell'ambito interno. La ragione di ciò è la seguente:

Ogni volta che Python cerca un nome di variabile, controlla prima locals, quindi controlla globals e infine controlla builtins. Nell'ambito del modulo , locals e globals si trova l'oggetto del dizionario SAME. Quindi la proposizione x = 5 nello scope del modulo inserirà x nel dizionario locals, che è anche il dizionario globals. Ora, una funzione definita nello scope del modulo che deve cercare x non troverà x all'interno dell'ambito di funzione locals, perché Python non copia i locus del modulo di visibilità in locals di scope di funzioni. Ma questo normalmente non è un problema, perché può trovare x in globals.

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

E 'solo con nidificati funzioni, che Python sembra copiare i locali esterno portata in locali interni-scope. (Sembra anche a farlo pigramente, solo se sono necessari nell'ambito di applicazione interna.)

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


Così il risultato di tutto questo è che, durante l'esecuzione di codice in modulo ambito, si deve assicurati che il tuo dizionario globale e il dizionario locale siano l'oggetto SAME, altrimenti le funzioni nell'ambito del modulo non saranno in grado di accedere alle variabili del modulo.

Ma nel mio caso, NON VOGLIO che il dizionario globale e il dizionario locale siano gli stessi. Quindi ho bisogno di un modo per dire all'interprete Python che sto eseguendo il codice allo scopo della funzione. C'è un modo per farlo? Ho esaminato lo PyCompileFlags e gli argomenti aggiuntivi su PyEval_EvalCodeEx e non riesco a trovare alcun modo per farlo.

risposta

3

Python non copia effettivamente locals di ambito esterno in locals di ambito interno; la documentazione per gli stati locals:

Le variabili libere vengono restituite dai locals() quando vengono richiamati nei blocchi funzione, ma non nei blocchi di classe.

Qui le variabili "libere" si riferiscono a variabili chiuse da una funzione annidata. È una distinzione importante.

La correzione più semplice per la vostra situazione è solo quello di passare lo stesso oggetto dict come globals e locals:

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

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

In caso contrario, si può avvolgere il codice in una funzione e estrarlo dalla oggetto compilato:

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

@ Canale72 vedere sopra. – ecatmur