2012-09-04 18 views
13

La función API Python C PyEval_EvalCode le permite ejecutar el código Python compilado. Quiero ejecutar un bloque del código Python como si se estuviera ejecutando dentro del alcance de una función, de modo que tenga su propio diccionario de variables locales que no afecte el estado global.C Python: Ejecutando el código Python dentro de un contexto

Esto parece bastante fácil de hacer, ya PyEval_EvalCode permite proporcionar un local y global diccionario:

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

El problema me encuentro con que tiene que ver con la forma en Python busca los nombres de variables. Considere el siguiente código, que ejecuto con PyEval_EvalCode:

myvar = 300 
def func(): 
    return myvar 

func() 

Este código simple en realidad genera un error, ya que Python es incapaz de encontrar la variable myvar desde dentro func. Aunque myvar está en el diccionario local en el ámbito externo, Python no lo copia en el diccionario local en el ámbito interno. La razón para esto es la siguiente:

Cada vez que Python busca un nombre de variable, primero comprueba locals, luego comprueba globals, y finalmente comprueba builtins. En , el alcance del módulo, locals y globals son el MISMO objeto de diccionario. Por lo tanto, la declaración x = 5 en el alcance del módulo colocará x en el diccionario locals, que también es el diccionario globals. Ahora, una función definida en el alcance del módulo que necesita buscar x no encontrará x dentro de la función-scope locals, porque Python no copia locals de alcance de módulo en locales de ámbito de función. Pero esto normalmente no es un problema, porque puede encontrar x en globals.

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

Es sólo con anidados funciones, que Python parece copiar los locales de alcance exterior en los locales interior de alcance. (También parece hacerlo con pereza, sólo si son necesarios en el ámbito interno.)

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


Así, el resultado de todo esto es que en la ejecución de código en ámbito de módulo, tienes que asegúrese de que su diccionario global y su diccionario local sean el MISMO objeto, de lo contrario las funciones del alcance del módulo no podrán acceder a las variables del ámbito del módulo.

Pero en mi caso, NO QUIERO que el diccionario global y el diccionario local sean iguales. Entonces necesito alguna forma de decirle al intérprete de Python que estoy ejecutando el código en el alcance de la función. Hay alguna manera de hacer esto? Miré el PyCompileFlags, así como los argumentos adicionales al PyEval_EvalCodeEx y no puedo encontrar ninguna forma de hacerlo.

Respuesta

3

Python en realidad no copia locas de alcance externo en locales de ámbito interno; la documentación para locals indica:

Las variables gratuitas las devuelven los locales() cuando se llaman en bloques funcionales, pero no en bloques de clases.

Aquí las variables "gratuitas" se refieren a variables cerradas por una función anidada. Es una distinción importante.

La solución más simple para su situación es sólo para pasar el mismo objeto dict como globals y locals:

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

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

De lo contrario, se puede envolver el código en una función y extraerlo del objeto compilado:

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

@ Channel72 ver arriba. – ecatmur

Cuestiones relacionadas