2011-07-22 13 views
6

Quiero declarar una función de forma dinámica y quiero ajustar cualquier acceso a las variables globales O alternativamente definir qué variables son libres y ajustar el acceso a las variables libres.Python: cómo configurar dinámicamente el entorno de cierre de función

estoy jugando un poco con código como el siguiente:

class D: 
    def __init__(self): 
     self.d = {}  
    def __getitem__(self, k): 
     print "D get", k 
     return self.d[k] 
    def __setitem__(self, k, v): 
     print "D set", k, v 
     self.d[k] = v 
    def __getattr__(self, k): 
     print "D attr", k 
     raise AttributeError 

globalsDict = D() 

src = "def foo(): print x" 

compiled = compile(src, "<foo>", "exec") 
exec compiled in {}, globalsDict 

f = globalsDict["foo"] 
print(f) 

f() 

Esto produce la salida:

D set foo <function foo at 0x10f47b758> 
D get foo 
<function foo at 0x10f47b758> 
Traceback (most recent call last): 
    File "test_eval.py", line 40, in <module> 
    f() 
    File "<foo>", line 1, in foo 
NameError: global name 'x' is not defined 

Lo que quiero es de alguna manera coger el acceso a x con mi envoltorio dict-como D . ¿Cómo puedo hacer eso?

No quiero predefinir todas las variables globales (en este caso x) porque quiero poder cargarlas perezosamente.

+0

¿Funciona si x ya está en globalsDict? – phkahler

+0

@phkahler No, no es así. 'f' todavía usa el dict real. La forma de evitar esto no es ajustar 'f', sino ajustar' x' en un proxy de objeto. – agf

Respuesta

2

Lo que estamos buscando es objeto proxy.

Aquí es una receta para un proxy de objeto que soporta ganchos de llamadas pre y post:

http://code.activestate.com/recipes/366254-generic-proxy-object-with-beforeafter-method-hooks/

Crear una subclase que en realidad no se carga el objeto hasta la primera vez que el gancho es _pre llamado. Cualquier cosa que acceda al objeto causará que el objeto real se cargue, y todas las llamadas parecerán manejadas directamente por el objeto real.

+0

Esto todavía tiene la desventaja de que tengo que crear dicho proxy de objeto para cada variable global a la que se pueda acceder. Esto ya es lo que traté de evitar, hacer cualquier trabajo previo; en su lugar, quería hacerlo todo sobre la marcha. (Por ejemplo, envolviendo el dict global o más). – Albert

+0

Desafortunadamente, los métodos son llamados al destino, no a la fuente. Si realmente no tiene control sobre las llamadas 'f', su única opción es realizar un proxy para' x' de alguna manera. Si tiene muchas variables globales que deben cargarse perezosamente, entonces es probable que su problema sea que estén en una clase, donde puede usar descriptors/decorators/getters/setters/property fácilmente para usar proxy para los antiguos globales. he hecho en clase o atributos de instancia. – agf

1

probar esto

class GlobalDict(object): 

    def __init__(self, **kwargs): 
     self.d = kwargs 

    def __getitem__(self, key): 
     print 'getting', key 
     return self.d[key] 

    def __setitem__(self, key, value): 
     print 'setting', key, 'to', value 
     if hasattr(value, '__globals__'): 
      value.__globals__.update(self.d) 
     self.d[key] = value 
     for v in self.d.values(): 
      if v is not value: 
       if hasattr(v, '__globals__'): 
        v.__globals__.update(self.d) 

    def __delitem__(self, key): 
     print 'deling', key 
     del self.d[key] 
     for v in self.d.values(): 
      if hasattr(v, '__globals__'): 
       del v.__globals__[key] 

>>> gd = GlobalDict() 
>>> src = 'def foo(): print x' 
>>> compiled = compile(src, '<foo>', 'exec') 
>>> exec compiled in {}, gd 
setting foo to <function foo at 0x102223b18> 
>>> f = gd['foo'] 
getting foo 
>>> f 
<function foo at 0x102223b18> 
>>> f() # This one will throw an error 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "<foo>", line 1, in foo 
NameError: global name 'x' is not defined 
>>> gd['x'] = 1 
setting x to 1 
>>> f() 
1 
>>> del gd['x'] # removes 'x' from the globals of anything in gd 
>>> f() # Will now fail again 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "<foo>", line 1, in foo 
NameError: global name 'x' is not defined 
+0

Esto no es realmente lo que quiero. Quiero * dinámicamente * atrapar el acceso. (Esto es porque quiero cargar dinámicamente algunas de las variables perezosamente.) – Albert

Cuestiones relacionadas