如何在Python中使用类实例变量作为方法装饰器的参数?

时间:2009-08-05 08:49:25

标签: python decorator

如何在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()

6 个答案:

答案 0 :(得分:9)

它不会起作用;装饰器在类创建时间内调用,该时间早于创建实例( if )。因此,如果你的“装饰者”需要实例,你必须在实例化时进行“装饰”:

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

答案 1 :(得分:5)

你的“warper”函数实际上是一个装饰者,而不是一个整经者。你的“decorator1”函数是一个装饰器构造函数。如果你想在运行时访问self.var1,你必须制作一个warper而不是装饰器:

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

答案 2 :(得分:0)

在定义类时执行装饰器,因此您无法将实例变量传递给它。

答案 3 :(得分:0)

以下是我们过去常常这样做的方法。

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语句。

是的,这是啰嗦。但是,它也不涉及任何魔法。

答案 4 :(得分:0)

请勿尝试与装饰器打交道以解决此问题,这将使您的代码非常复杂且令人讨厌阅读。

您正在尝试在类创建时使用仅在实例化之后才可用的信息构造包装器。您可以在实例化时动态地构建装饰器,但是仍然需要将装饰器应用于方法的外部类级别将无法访问实例变量。

为了避免用装饰器解决该问题的麻烦,Python引入了(从2.7开始)专用的数据模型来解决这种特定类型的问题。它称为context manager,您可以仅使用生成器来实现它。

from contextlib import contextmanager

def lock_file(file):
    print('File %s locked' % file)

def unlock_file(file):
    print('File %s unlocked' % file)

@contextmanager
def file_locked(arg1):
    lock_file(arg1)
    yield
    unlock_file(arg1)

class Foo(object):
    def __init__(self, arg1):
        self.var1 = arg1

    def method1(self):
        with file_locked(self.var1) as f:
            print "method1"

foo = Foo("abc")
foo.method1()

答案 5 :(得分:0)

嵌套方法成功了,我在使用它时再次传递自我时遇到了问题。后来我用 lambda 封装了函数赋值来解决这个问题。

https://gist.github.com/kirankotari/e6959e74316352bfd06afa4d5ab8bd52