在装饰器类中,访问包含被装饰方法的类实例
我有一个装饰器,它的作用是在调用带有 @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
的实例。