Comment utiliser une variable d'instance de classe comme argument d'un décorateur de méthode en Python?
Question
Comment utiliser une variable d'instance de classe comme argument d'un décorateur de méthode en Python? Ce qui suit est un exemple minimal qui montre ce que je tente de faire. Cela échoue évidemment car la fonction décorateur n'a pas accès à la référence à l'instance et je ne sais pas comment obtenir l'accès à la référence du décorateur.
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()
La solution
Ça ne va pas marcher; le décorateur est appelé lors de la création de la classe , longtemps avant la création d'une instance ( si , cela se produit). Donc, si votre "décorateur" a besoin de l'instance, vous devez faire le "décor" & ";" au moment de l'instanciation:
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()
Notez que j'ai changé les noms des fonctions en fonction de leur signification. le "décorateur" réel, c’est-à-dire que la fonction qui (potentiellement) modifie la méthode, est wrapper
dans votre cas, et non decorator1
.
Autres conseils
Votre fonction "warper" est en réalité un décorateur, et non un warper. Votre fonction «décorateur1» est un constructeur de décorateur. Si vous voulez avoir accès à self.var1 au runtime, vous devez faire un warper pas décorateur:
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 vous souhaitez avoir un décorateur plus générique, il est préférable de déclarer une classe appelable:
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
Utilisation de:
@decorator("var1")
Le décorateur est exécuté lorsque la classe est définie. Vous ne pouvez donc pas lui transmettre de variable d'instance.
Voici comment nous avions l'habitude de faire cela dans les temps anciens.
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
Désormais, une sous-classe n'a plus qu'à remplacer do_method1
pour bénéficier des avantages du "wrapping". Fait à l'ancienne, sans instruction avec
.
Oui, c'est long. Cependant, cela n’implique aucune magie non plus.