在装饰器类中,访问包含被装饰方法的类实例

8 投票
3 回答
2779 浏览
提问于 2025-04-16 11:46

我有一个装饰器,它的作用是在调用带有 @saveconfig 的方法后,保存一个配置文件:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *args):
        self.f(object, *args)
        # Here i want to access "cfg" defined in pbtools
        print "Saving configuration"

我在下面这个类里面使用这个装饰器。在调用 createkvm 方法后,配置对象 self.cfg 应该在装饰器里面被保存:

class pbtools()
    def __init__(self):
        self.configfile = open("pbt.properties", 'r+')
        # This variable should be available inside my decorator
        self.cfg = ConfigObj(infile = self.configfile)

    @saveconfig
    def createkvm(self):
        print "creating kvm"

我遇到的问题是,我需要在装饰器 saveconfig 里面访问对象变量 self.cfg。我最开始的简单想法是给装饰器加一个参数,用来传递对象,比如 @saveconfig(self),但这样不行。

我该如何在装饰器里面访问方法所在对象的变量呢?我是不是需要在同一个类里面定义这个装饰器才能访问?

3 个回答

0

你可以用一个简单的函数来实现你想要的功能:

def saveconfig(f):
    # this method replaces the decorated, so `self` will be the pbtools instance
    def wrapped(self, *args):
        f(self, *args)
        # Here i want to access "cfg" defined in pbtools
        print "Saving configuration", self.cfg
    return wrapped

如果 saveconfig 必须是一个类的话,那你就需要Sven的解决方案。

4

你把 object 作为 self 传给了装饰过的方法。问题是,你不能轻易获取 self,因为 Python 看到的是装饰后的方法,这个方法现在变成了一个对象,不再被视为一个方法(在调用时应该传入 self,或者更一般地说,应该像一个属性一样返回一个绑定的方法)。不过,你可以通过其他方式解决这个问题,正如 @Sven Marnach 指出的那样。

不过,你可以很简单地重写这个装饰器,不用类,而是用闭包(这样写会短一些,也能解决上面提到的问题):

def saveconfig(f):
    @functools.wraps(f) # to preserve name, docstring, etc.
    def wrapper(*args, **kwargs): # **kwargs for compability with functions that use them
        f(*args, **kwargs)
        # save config
    return wrapper

其他说明:

  • 术语混淆:在这个例子中没有类变量。类变量应该是 x = ...,并且缩进到方法定义的同一级别,所有实例都会共享这个变量(具体来说,它会是 pbtools 对象的一个属性)——而 self 上的所有东西都是实例属性。
  • 在类定义的时候(当你定义方法、应用装饰器等)是没有 self 的!
11

你需要让你的装饰器类像一个描述符一样工作,这样才能访问到实例:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            print "Saving configuration"
            print instance.cfg
            return self.f(instance, *args)
        return wrapper

你的代码把object作为第一个参数传给self.f(),而实际上应该传的是pbtools的实例。

撰写回答