Comment utiliser une variable d'instance de classe comme argument d'un décorateur de méthode en Python?

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

  •  22-07-2019
  •  | 
  •  

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()
Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top