¿Cómo puedo usar una variable de instancia de clase como argumento para un decorador de métodos en Python?

StackOverflow https://stackoverflow.com/questions/1231950

  •  22-07-2019
  •  | 
  •  

Pregunta

¿Cómo puedo usar una variable de instancia de clase como argumento para un decorador de métodos en Python? El siguiente es un ejemplo mínimo que muestra lo que estoy tratando de hacer. Obviamente falla ya que la función de decorador no tiene acceso a la referencia a la instancia y no tengo idea de cómo obtener acceso a la referencia desde el decorador.

def decorator1(arg1):
    def wrapper(function):
        print "decorator argument: %s" % arg1
        return function
    return wrapper

class Foo(object):
    def __init__(self, arg1):
        self.var1 = arg1

    @decorator1(self.var1)
    def method1(self):
        print "method1"

foo = Foo("abc")
foo.method1()
¿Fue útil?

Solución

No va a funcionar; se llama al decorador durante el tiempo de creación de clase , que es mucho antes de que se cree una instancia ( if que ocurra alguna vez). Entonces, si tu " decorador " necesita la instancia, tienes que hacer la "decoración" en el momento de la instanciación:

def get_decorator(arg1):
    def my_decorator(function):
        print "get_decorator argument: %s" % arg1
        return function
    return my_decorator

class Foo(object):
    def __init__(self, arg1):
        self.var1 = arg1
        self.method1 = get_decorator(self.var1)(self.method1)

    def method1(self):
        print "method1"

foo = Foo("abc")
foo.method1()

Tenga en cuenta que cambié los nombres de las funciones de acuerdo con sus significados; el "decorador" real, es decir, la función que (potencialmente) modifica el método, es wrapper en su caso, no decorator1 .

Otros consejos

Su & # 8220; warper & # 8221; La función es en realidad un decorador, en lugar de un warper. Su & ??# 8220; decorador1 & # 8221; La función es un constructor decorador. Si desea tener acceso a self.var1 en tiempo de ejecución, debe hacer un warper no decorador:

def decorator(function):
  def wrapper(self,*args,**kwargs):
    print "Doing something with self.var1==%s" % self.var1
    return function(self,*args,**kwargs)
  return wrapper

class Foo(object):
  def __init__(self, arg1):
    self.var1 = arg1

  @decorator
  def method1(self):
    print "method1"

foo = Foo("abc")
foo.method1()

Si desea tener un decorador más genérico, es mejor declarar una clase invocable:

class decorator:
  def __init__(self,varname):
      self.varname = varname
  def __call__(self,function):
    varname=self.varname
    def wrapper(self,*args,**kwargs):
      print "Doing something with self.%s==%s" % (varname,getattr(self,varname))
      return function(self,*args,**kwargs)
    return wrapper

Uso:

  @decorator("var1")

El decorador se ejecuta cuando se define la clase, por lo que no puede pasarle una variable de instancia.

Así es como solíamos hacer esto en los viejos tiempos.

class Foo(object):
    def __init__(self, arg1):
        self.var1 = arg1

    def method1(self):
        self.lock()
        try:
            self.do_method1()
        except Exception:
            pass # Might want to log this
        finally:
            self.unlock()

    def do_method1(self):
        print "method1"

    def lock(self):
        print "locking: %s" % self.arg1

    def unlock(self):
        print "unlocking: %s" % self.arg1

Ahora, una subclase solo necesita anular do_method1 para obtener los beneficios del " wrapping " ;. Hecho a la antigua usanza, sin ninguna instrucción with .

Sí, es de largo aliento. Sin embargo, tampoco implica ninguna magia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top