2012-02-08 15 views

Respuesta

7

Ver https://stackoverflow.com/a/4249347/224295, http://code.activestate.com/recipes/577283-decorator-to-expose-local-variables-of-a-function-/

Ejemplo de trabajo:

import sys 

class persistent_locals(object): 
    def __init__(self, func): 
     self._locals = {} 
     self.func = func 

    def __call__(self, *args, **kwargs): 
     def tracer(frame, event, arg): 
      if event=='return': 
       self._locals = frame.f_locals.copy() 

     # tracer is activated on next call, return or exception 
     sys.setprofile(tracer) 
     try: 
      # trace the function call 
      res = self.func(*args, **kwargs) 
     finally: 
      # disable tracer and replace with old one 
      sys.setprofile(None) 
     return res 

    def clear_locals(self): 
     self._locals = {} 

    @property 
    def locals(self): 
     return self._locals 

@persistent_locals 
def func(): 
    local1 = 1 
    local2 = 2 

func() 
print func.locals 
+0

que funciona. Sin embargo, estoy de acuerdo en que es una mala idea, pero estaba interesado en ver si es posible. – Harel

+0

Gran truco. Estoy haciendo un depurador que tiene que atrapar a escondidas internos de una función decorada y para eso es legítimamente útil. Una cosa a tener en cuenta es que se llama tracer() no solo al retorno de la función decorada, sino también a los retornos de todas las funciones llamadas desde dentro de esa función. Como la función decorada siempre tiene el último retorno (y, por lo tanto, establece _locales por última vez) no importa en este caso. – Peter

-2

En lugar de perder el tiempo con elementos internos de la función (que se romperá si la función decorada es nativo), escribir su propio envoltorio, así:

def myDecorator(f): 
    def wrapper(*args, **kwargs): 
    print('Arguments to this function %r/%r' % (args, kwargs)) 
    return f(*args, **kwargs) 
    return wrapper 
3

<edit> Me acabo de dar cuenta leí mal la pregunta y que no está tratando de obtener atributos de función, sino valores de variables locales de la función. Lo que desea hacer no es posible porque esas variables locales no se crean hasta que se ejecuta la función, y el alcance local para la función se elimina tan pronto como la función devuelve o genera una excepción.

dejo mi respuesta original, ya que potencialmente podría reescribir su función para utilizar atributos en lugar de las variables locales y aún usar el decorador de hacer efectivamente lo que quiere.

Sería de gran ayuda para que usted pueda publicar lo que han intentado actualmente y algunos ejemplos de llamadas con el resultado esperado si funcionaba correctamente. </edit>

Cuando necesita una función con atributos, generalmente es una buena idea usar una clase invocable en lugar de una definición de función normal.

Aquí es un ejemplo de un decorador cuando la capa es una clase exigible, lo que permite al decorador para acceder a las variables con facilidad, ya que son las variables de instancia de la clase contenedora:

def deco(func): 
    class Wrapper(object): 
     def __init__(self): 
      self.foo = None 
     def __call__(self, *args): 
      print 'old foo:', self.foo 
      result = func(*args) 
      print 'new foo:', self.foo 
      return result 
    return Wrapper() 

@deco 
def my_func(new_foo): 
    my_func.foo = new_foo 

que se traduce en my_func comportarse de esta manera:

>>> my_func('test') 
old foo: None 
new foo: test 
>>> my_func.foo 
'test' 
>>> my_func(42) 
old foo: test 
new foo: 42 
>>> my_func.foo 
42 
-1

no, no es posible acceder a variables dentro de la función envuelto, porque (como FJ señaló en su edición) que no existen en el momento en que el decorador está en su alcance.

5

Lo que estás pidiendo no tiene sentido.

variables locales de una función no tienen valores todo el tiempo. Considere esta función:

def foo(x): 
    y = x + 27 
    return y 

¿Cuál es el valor de la variable local yfoo 's? No puede responder, la pregunta ni siquiera tiene sentido hasta que llame al foo (e incluso entonces, no hasta que se ejecute la línea y = x + 27).

E incluso entonces no es lo que y que no tenga un valor en este momento, podría haber cualquier número de "en vuelo" ejecuciones de foo. Podría haber subprocesos ejecutando foo, o foo podría ser recursivo (posiblemente indirectamente) para que haya más de una llamada en progreso incluso en una única pila de llamadas. O foo podría ser un generador, por lo que podría haber muchas ejecuciones en vuelo foo incluso sin recurrencia (es decir,no todos son accesibles desde el alcance más externo foo). Entonces, ¿cuál y obtendría el valor de?

El valor de y en foo simplemente no es un concepto bien definido a menos que usted esté hablando dentro del alcance de foo.


Dada la flexibilidad de Python, estoy bastante seguro de que es posible hacer pila marco de la introspección y encontrar un marco de pila para foo cuando no se vive actualmente y tire de los valores de sus variables locales en ese momento. Esto sería bastante difícil (si no imposible) de hacer con un decorador, porque (a menos que foo sea un generador) el decorador solo puede agregar el código de envoltura "alrededor" foo, lo que significa que el código controlado por el decorador se ejecuta antes y después de foo , por lo que solo obtiene el control cuando el marco de pila foo no existe.

No voy a dar consejos específicos sobre cómo hacerlo, porque no sé cómo hacerlo. Sin embargo, parece que es casi una mala idea, a menos que esté escribiendo un depurador.

Cuestiones relacionadas