我想创建一个装饰器来创建一个使用对象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
属性定义装饰方法?要么是完美的。
答案 0 :(得分:1)
因为装饰者只是用来说
的语法糖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)
答案 1 :(得分:1)
要弄明白你到底是什么有点困难。列表和__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
答案 2 :(得分:0)
你的写作风格真的难以阅读。正常句子是你的一半: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)
答案 3 :(得分:0)
好的,根据manifest的代码,我有一个解决方案。
正如你所看到的,pydoc仍然会有关于装饰物的文档:
class A(__builtin__.object) | Methods defined here: | | f(*args, **kwargs) | f(self, x) | something
同样,带有装饰方法的类的每个实例都将具有不同的obj
。每个函数都有自己的obj
。
<__main__.A object at 0x7c209bee4a50>.f(123) obj of <__main__.A object at 0x7c209bee4a50>.f is 136479497243752 <__main__.A object at 0x7c209bee4a10>.f(123) obj of <__main__.A object at 0x7c209bee4a10>.f is 136479497250720 <__main__.A object at 0x7c209bee4a90>.f(123) obj of <__main__.A object at 0x7c209bee4a90>.f is 136479497446824 myfunc obj of myfunc is 136479497243392 myfunc obj of myfunc is 136479497243392 myfunc2 obj of myfunc2 is 136479497245688 myfunc2 obj of myfunc2 is 136479497245688 myfunc3 obj of myfunc3 is 136479497246408 myfunc3 obj of myfunc3 is 136479497246408
以下是代码:
__all__ = ['dec', 'A']
from functools import wraps
import inspect
def dec(cls=None):
# cls will be closed in subdec
def subdec(func):
# closed in wrapper, guaranteed to be unique per decorator evaluation
obj = []
@wraps(func)
def wrapper(*args, **kwargs):
if (args and type(args[0]) == cls):
instance = args[0]
# We will attach to instance a dict _objs of
# function_name:obj. This way we don't pollute the namespace
# when decorating many functions.
# Alternatively, you could make a dict external to instance
# of instance:{function_name:obj}, but that takes more work
# because you need to make it not prevent garbage collection
# of instance.
if not hasattr(instance, "_objs"):
instance._objs = {}
if func.__name__ not in instance._objs:
instance._objs[func.__name__] = []
func(*args, **kwargs) # This is only used to check the arity.
# My real code is all to do with
# manipulating obj.
print "obj of %s.%s is %s" % (
instance,
func.__name__,
id(instance._objs[func.__name__])
)
else:
# Functions are identified by the closed obj
func(*args, **kwargs)
print "obj of %s is %s" % (func.__name__, id(obj))
# Find function/method signature and prepend it to the new object's doc
try:
doc = inspect.getsourcelines(func)
except IOError:
line = "<unable to fetch definition>"
else:
line = '@'
i = 0
while line.lstrip(' ').startswith("@"):
try:
line = doc[0][i]
except IndexError:
line = "<unable to fetch definition>"
i += 1
line = line.rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
wrapper.__doc__ = line + "\n" + (func.__doc__ or '')
return wrapper
return subdec
class A(object):
def f(self, x):
"""something"""
print '%s.f(%s)' % (self, x)
A.f = dec(A)(A.f)
@dec()
def myfunc():
print "myfunc"
@dec()
def myfunc2():
print "myfunc2"
@dec()
def myfunc3():
print "myfunc3"
if __name__ == "__main__":
a, b, c = A(), A(), A()
# a, b, and c each have their own instance of obj:
a.f(123)
b.f(123)
c.f(123)
myfunc()
myfunc()
myfunc2()
myfunc2()
myfunc3()
myfunc3()
添加的外部装饰器cls
仅用于获取类的标识,因此装饰函数可以确定天气它是一个函数或方法。虽然我不确定[多重]继承会有多好...也许显示检查该部分更好的想法。