从属性包装方法返回修改后的docstring

时间:2015-05-20 04:09:59

标签: python

我有一个属性包装类:

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。我想以某种方式修补内容,以便包装函数中的原始文档字符串显示出来(更好的是,我想稍微修改它以包含它被包装的事实)。有可能这样做吗?

2 个答案:

答案 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'