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

时间:2011-02-13 22:50:01

标签: python decorator

我有以下装饰器,它在调用用@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"

我的问题是我需要访问装饰器self.cfg内的对象变量saveconfig。第一个天真的方法是向装饰器添加一个参数来保存对象,如@saveconfig(self),但这不起作用。

如何在装饰器中访问方法主机的对象变量?我是否必须在同一个类中定义装饰器才能获得访问权限?

3 个答案:

答案 0 :(得分:11)

您必须使您的装饰器类表现为descriptor才能访问该实例:

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个实例。

答案 1 :(得分: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

答案 2 :(得分: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的解决方案。