我有一个属性包装类:
import functools
class wrapper(object):
def __init__(self, function):
self.function = function
functools.update_wrapper(self, function)
def __call__(self, *args, **kwargs):
# Do some stuff here first
return self.function(self.obj, *args, **kwargs)
def __get__(self, instance, owner):
self.cls = owner
self.obj = instance
return self.__call__
然后我可以包装一些方法Bar.foo:
class Bar:
@wrapper
def foo(self):
""" This is the docstring I want to see """
pass
现在,如果我创建Bar的一些实例,并查看Bar.foo,它显然是一个包装器,它没有docstring。我想以某种方式修补内容,以便包装函数中的原始文档字符串显示出来(更好的是,我想稍微修改它以包含它被包装的事实)。有可能这样做吗?
答案 0 :(得分:1)
事实证明,类形式的装饰器与“通常”装饰器(以函数形式)的工作方式不同。 update_wrapper
更新了装饰器实例的属性,但是当你将该装饰器应用于类的方法时,__doc__
和其他属性实际上将从装饰器类的__call__
方法中获取(因为你从__call__
返回__get__
。例如:
import functools
class wrapper1(object):
"""This is the wrapper1 class"""
def __init__(self, wrapped):
"""This is the wrapper1.__init__"""
self.wrapped = wrapped
functools.update_wrapper(self, wrapped)
def __call__(self, *args, **kwargs):
"""This is the wrapper1.__call__"""
print "__call__ of wrapper1.__call__"
if hasattr(self, 'obj'):
return self.wrapped(self.obj, *args, **kwargs)
return self.wrapped(*args, **kwargs)
def __get__(self, instance, owner):
self.cls = owner
self.obj = instance
return self.__call__
def wrapper2(wrapped):
@functools.wraps(wrapped)
def _wrapper(*args, **kwargs):
print "_wrapper of wrapper2 decorator"
return wrapped(*args, **kwargs)
return _wrapper
@wrapper1
def f1():
"""The f1 function"""
print "f1 call"
@wrapper2
def f2():
"""The f2 function"""
print "f2 call"
print "call of f1: '{}'".format(f1.__doc__)
f1()
print "call of f2: '{}'".format(f2.__doc__)
f2()
class A(object):
def __init__(self, desc):
self.desc = desc
@wrapper1
def m1(self):
"""The A.m1 method"""
print "A.m1 call: {}".format(self.desc)
@wrapper2
def m2(self):
"""The A.m2 method"""
print "A.m2 call: {}".format(self.desc)
a = A('Hello!')
print "Call of A.m2: {}: {}".format(a.m2.__doc__, A.m2.__doc__)
a.m2()
print "Call of A.m1: {}: {}".format(a.m1.__doc__, A.m1.__doc__)
a.m1()
最后两行打印出来:
Call of A.m1: This is the wrapper1.__call__: This is the wrapper1.__call__
__call__ of wrapper1.__call__
A.m1 call: Hello!
如果装饰器的形式并不重要,那么使用装饰器作为def wrapper(wrapped):
。它更简单,更清晰。
但是如果你仍然想出于某种原因使用类形式的装饰器,那么将__get__
方法更改为返回self
,它将修复不需要的行为:
def __get__(self, instance, owner):
self.cls = owner
self.obj = instance
return self
答案 1 :(得分:1)
正如Aleksandr指出his answer __doc__
将取自__call__
类wrapper
方法
Documentation个,__doc__
是用户定义方法的只读属性,但函数__doc__
不是。
请在functools.update_wrapper
上调用self.__call__.__func__
而不是self
方法内的__init__
。
如果您想修改原始文档字符串,则可以使用以下代码
进行修改self.__call__.__func__.__doc__ = self.__call__.__doc__ + ' added later'