Come posso usare una variabile di istanza di classe come argomento per un decoratore di metodi in Python?

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

  •  22-07-2019
  •  | 
  •  

Domanda

Come posso usare una variabile di istanza di classe come argomento per un decoratore di metodi in Python? Il seguente esempio minimo mostra cosa sto cercando di fare. Ovviamente fallisce perché la funzione decoratore non ha accesso al riferimento all'istanza e non ho idea di come accedere al riferimento dal decoratore.

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()
È stato utile?

Soluzione

Non funzionerà; il decoratore viene chiamato durante la creazione della classe , che è molto prima che venga creata un'istanza ( se che accada mai). Quindi se il tuo "decoratore" ha bisogno dell'istanza, devi fare la "decorazione" al momento dell'istanza:

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

Nota che ho cambiato i nomi delle funzioni in base al loro significato; l'attuale "decoratore", ovvero la funzione che (potenzialmente) modifica il metodo, è wrapper nel tuo caso, non decorator1 .

Altri suggerimenti

Il tuo & # 8220; ordito & # 8221; la funzione è in realtà un decoratore, piuttosto che un ordito. Il tuo & # 8220; decoratore1 & # 8221; la funzione è un costruttore di decoratori. Se vuoi avere accesso a self.var1 in runtime devi creare un warper non un decoratore:

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 vuoi avere un decoratore più generico, è meglio dichiarare una classe richiamabile:

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

Utilizzo:

  @decorator("var1")

Il decoratore viene eseguito quando viene definita la classe, quindi non è possibile passargli una variabile di istanza.

Ecco come lo facevamo in passato.

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

Ora, una sottoclasse deve solo sovrascrivere do_method1 per ottenere i vantaggi del "wrapping". Fatto alla vecchia maniera, senza con .

Sì, è prolisso. Tuttavia, non comporta nessuna magia.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top