Как я могу использовать переменную экземпляра класса как аргумент для декоратора метода в Python? - PullRequest
5 голосов
/ 05 августа 2009

Как я могу использовать переменную экземпляра класса в качестве аргумента для декоратора метода в Python? Ниже приведен минимальный пример того, что я пытаюсь сделать. Очевидно, что это не работает, так как функция декоратора не имеет доступа к ссылке на экземпляр, и я не знаю, как получить доступ к ссылке от декоратора.

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

Ответы [ 4 ]

9 голосов
/ 05 августа 2009

Это не сработает; декоратор вызывается во время создания класса времени, то есть задолго до создания экземпляра (, если произойдет). Поэтому, если вашему «декоратору» нужен экземпляр, вы должны выполнить «декорирование» во время создания экземпляра:

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

Обратите внимание, что я изменил имена функций в соответствии с их значениями; фактический «декоратор», то есть функция, которая (потенциально) модифицирует метод, в вашем случае равен wrapper, а не decorator1.

5 голосов
/ 05 августа 2009

Ваша функция «деформирования» на самом деле является декоратором, а не деформированием. Ваша функция «decorator1» является конструктором декоратора. Если вы хотите иметь доступ к self.var1 во время выполнения, вам нужно сделать декоратор не декоратором:

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

Если вы хотите иметь более универсальный декоратор, лучше объявить вызываемый класс:

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

Использование:

  @decorator("var1")
0 голосов
/ 05 августа 2009

Вот как мы это делали в старину.

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

Теперь подклассу нужно только переопределить do_method1, чтобы получить преимущества «обертывания». Сделано по-старому, без with заявления.

Да, это скучно. Тем не менее, он также не включает никакой магии.

0 голосов
/ 05 августа 2009

Декоратор выполняется, когда класс определен, поэтому вы не можете передать ему переменную экземпляра.

...