基本描述符/装饰器
你只需要记住你应该装饰哪个功能。您的函数是在中创建的__get__
,因此将包装器用作装饰器无济于事,相反,您需要在__get__
方法中应用它。顺便说一句,您可以使用其中一个functools.update_wrapper
或decorators.decorator
为此。它们的工作方式非常相似,只是您必须保留decorators.decorator
whilefunctools.update_wrapper
返回的结果None
。两者都有签名f(wrapper, wrapped)
。
from functools import update_wrapper
class class_or_instance(object):
def __init__(self, fn):
self.fn = fn
def __get__(self, obj, cls):
if obj is not None:
f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
else:
f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
# update the function to have the correct metadata
update_wrapper(f, self.fn)
return f
class A(object):
@class_or_instance
def func1(self,*args):
"""some docstring"""
pass
现在,如果你这样做:
print A.func1.__doc__
你会看到“一些文档字符串”。耶!
缓存属性装饰器
这里的关键是您只能影响返回的内容。由于class_or_instance
实际上并不作为函数,所以你用它做什么并不重要。请记住,此方法会导致函数每次都被反弹。我建议你添加一点魔法,并在第一次调用后绑定/缓存函数,这实际上只涉及添加setattr
调用。
from functools import update_wrapper
import types
class class_or_instance(object):
# having optional func in case is passed something that doesn't have a correct __name__
# (like a lambda function)
def __init__(self, name_or_func):
self.fn = fn
self.name = fn.__name__
def __get__(self, obj, cls):
print "GET!!!"
if obj is not None:
f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
update_wrapper(f, self.fn)
setattr(obj, self.name, types.MethodType(f, obj, obj.__class__))
else:
f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
update_wrapper(f, self.fn)
return f
然后我们可以对其进行测试...neato:
A.func1 #GET!!!
obj = A()
obj.func1 #GET!!!
obj.func1 is obj.func1 # True
A.func1 # GET!!!
obj2 = A()
obj2.func1 is not obj.fun1 # True + GET!!!