Como posso usar uma variável de instância de classe como um argumento para um decorador método em Python?
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()
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.