带有实例化变量的Python装饰器?
我想做一个装饰器,它可以创建一个新的函数或方法,并使用一个对象 obj
。如果被装饰的对象是一个函数,那么在创建这个函数的时候,obj
必须被实例化。如果被装饰的对象是一个方法,那么每次调用这个方法的类实例都要有一个新的 obj
实例。我不能把装饰器放在 __init__
里,因为装饰器会修改函数的文档说明。目前我有的代码是这样的,但它只会实例化一次 time
,这不是我想要的:
__all__ = ['dec', 'A']
from time import time
import inspect
def dec(f):
obj = time() # want to set on object instantiation
def new(*args, **kwargs):
f(*args, **kwargs) # Validate against definition so it doesn't go
# out of sync
print obj
# ...
try:
d = inspect.getsourcelines(f)
except IOError:
d = "<unable to fetch definition>"
else:
d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
new.__doc__ = d + "\n" + (f.__doc__ or '')
return new
class A(object):
@dec
def f(self, x):
"""something"""
print '%s.f(%s)' % (self, x)
if __name__ == '__main__':
A().f(123)
A().f(123)
A().f(123)
我想出的解决办法是检查传给装饰器的对象是否有一个参数 self
,如果有的话,就返回一个方法,这个方法会把 obj
绑定到 self
上(如果还没有绑定的话),然后使用 self.obj
。如果传给装饰器的对象没有 self
参数,那就直接在装饰器里实例化 obj
,并返回一个使用这个对象的函数。
不过……我说的这些对我来说并不太管用,因为在我真正的装饰器里,我返回的是一个从 list
继承的对象,并且它有一个 __call__
属性。而且在被这个装饰器装饰的对象中,self
甚至没有被定义,因为它们并不使用自己的实例变量(我真正装饰的只是一些事件,这些事件会被外部对象订阅,事件有文档说明)。
补充:其实,如果有办法让一个 list
的子类实例绑定到一个实例上,这样它的 __call__
属性就能隐式地接收到类实例(就像普通实例方法那样),那将是一个完美的解决方案,这也是我最初想要弄明白的。不过,也许还有更好的解决办法,这样我就不需要用 self
属性来定义被装饰的方法了?这两种方法都可以。
4 个回答
你的写作风格真让人难以阅读。普通的句子长度只有你的一半 :P
你是想这样吗?
__all__ = ['dec', 'A']
from time import time, sleep
import inspect
def dec(f):
def new(self, *args, **kwargs):
print self.initiated # print the time the objecte was initiated ...
return f(self, *args, **kwargs) # Validate against definition so it doesn't go
# out of sync
try:
d = inspect.getsourcelines(f)
except IOError:
d = "<unable to fetch definition>"
else:
d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
new.__doc__ = d + "\n" + (f.__doc__ or '')
return new
class A(object):
def __init__(self):
self.initiated = time() # save the time the object was initiated
@dec
def f(self, x):
"""something"""
print '%s.f(%s)' % (self, x)
if __name__ == '__main__':
A().f(123)
sleep(1)
A().f(123)
sleep(1)
A().f(123)
要搞清楚你到底想要什么,有点困难。你提到的列表和__call__的内容让我有点困惑,所以我主要关注你第一段的内容:
__all__ = ['dec', 'A']
from types import InstanceType
from functools import wraps
import inspect
def dec(func):
#get the sig of the function
sig = []
@wraps(func)
def wrapper(*args, **kwargs):
ret = None
#if this is a method belonging to an object...
if args and getattr(args[0], func.__name__, None):
instance, args = args[0], args[1:]
#if sig of object is not already set
if not hasattr(instance, "sig"):
instance.sig = []
ret = func(instance, *args, **kwargs)
print "Sig of %s is %s" % (func.__name__, id(instance.sig))
#else this is a function
else:
ret = func(*args, **kwargs)
print "Sig of %s is %s" % (func.__name__, id(sig))
return ret
#modify the doc string
try:
docs = inspect.getsourcelines(func)
except:
docs = "<unable to fetch defintion>"
else:
docs = docs[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
wrapper.__doc__ = docs + "\n" + (func.__doc__ or '')
return wrapper
class A(object):
def __init__(self):
super(A, self).__init__()
@dec
def f(self, x):
"""something"""
print '%s.f(%s)' % (self, x)
@dec
def myfunc():
print "myfunc"
@dec
def myfunc2():
print "myfunc2"
@dec
def myfunc3():
print "myfunc3"
if __name__ == "__main__":
list = []
for x in xrange(3):
list.append(A())
[a.f(123) for a in list]
myfunc()
myfunc()
myfunc2()
myfunc2()
myfunc3()
myfunc3()
输出结果:
<__main__.A object at 0x00B9F2D0>.f(123)
Sig of f is 11932616
<__main__.A object at 0x00B9F430>.f(123)
Sig of f is 11925464
<__main__.A object at 0x00B9F450>.f(123)
Sig of f is 11918112
myfunc
Sig of myfunc is 11925624
myfunc
Sig of myfunc is 11925624
myfunc2
Sig of myfunc2 is 11794592
myfunc2
Sig of myfunc2 is 11794592
myfunc3
Sig of myfunc3 is 11925144
myfunc3
Sig of myfunc3 is 11925144
因为装饰器其实就是一种语法上的简化,意思是说
def func():
...
func = decorator(func)
那为什么不直接在对象的构造函数里做呢?
class A(object):
def __init__(self):
# apply decorator at instance creation
self.f = dec(self.f)
def f(self, x):
"""something"""
print '%s.f(%s)' % (self, x)