Como posso usar uma variável de instância de classe como um argumento para um decorador método em Python?

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

  •  22-07-2019
  •  | 
  •  

Pergunta

Como posso usar uma variável de instância de classe como um argumento para um decorador método em Python? O seguinte é uma exemplo mínimo mostra o que estou tentando fazer. É, obviamente, não como a função decorador não tem acesso à referência à instância e não tenho idéia de como obter acesso à referência do 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()
Foi útil?

Solução

Não vai para o trabalho; o decorador é chamado durante criação da classe tempo, o que é muito antes de uma instância é criada ( se que nunca acontece). Portanto, se seu "decorador" precisa do exemplo, você tem que fazer a "decorar" em tempo de instanciação:

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()

Note que eu mudei os nomes das funções de acordo com os seus significados; o "decorador" real, ou seja, a função que (potencialmente) modifica o método, é wrapper no seu caso, não decorator1.

Outras dicas

A sua função “warper” é na verdade um decorador, ao invés de um warper. Sua função “decorator1” é um construtor decorador. Se você quiser ter acesso a self.var1 em tempo de execução que você tem que fazer uma warper não 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()

Se você quiser ter decorador mais genérico, é melhor idéia para declarar uma classe que pode ser chamado:

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

Usando:

  @decorator("var1")

O decorador é executado quando a classe é definida, para que você não pode passar uma variável de instância a ele.

Aqui está como nós costumava fazer isso nos dias antigos.

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

Agora, uma subclasse só precisa do_method1 o override para obter os benefícios do "embrulho". Feito da maneira antiga, sem qualquer declaração with.

Sim, é prolixo. No entanto, não envolve qualquer tipo de magia, qualquer um.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top