为什么Python装饰器会丢失函数属性__doc__?

2 投票
2 回答
662 浏览
提问于 2025-04-18 02:48
def decor(fun):
    def living(*args, **kw):
        return fun(*args, **kw)
    return living

@decor
def test():
    '''function doc'''
    pass

print test.__doc__

为什么结果是 None 呢?我在使用装饰器的时候发生了什么?谢谢大家的回答!

2 个回答

0

这是因为你的装饰器实际上是在替换你的函数。你需要使用 functools.wraps() 来保存被装饰函数的一些内部信息,比如 __name____doc__

你可以通过给你的装饰器函数 living() 添加一个文档字符串来简单测试这一点:

>>> def decor(fun):
...     def living(*args, **kw):
...         """This is the decorator for living()"""
...         return fun(*args, **kw)
...     return living
... 
>>> @decor
... def test():
...     """function doc"""
...     pass
... 
>>> test.__doc__
'This is the decorator for living()'

这是来自 functools.wraps() 文档的例子,它保存了被包装函数的名称和文档字符串。

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print 'Calling decorated function'
...         return f(*args, **kwds)
...     return wrapper
...
>>> @my_decorator
... def example():
...     """Docstring"""
...     print 'Called example function'
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
1

当你把一个函数放在装饰器里时:

@decor
def test:

你得到的是装饰器创建的函数(在这个例子中是living),这个函数没有原来的文档字符串等信息。它并不是“丢失”了这些数据,而是living根本就没有这些信息!

你可以通过使用functools.wraps来解决这个问题:

from functools import wraps

def decor(fun):
    @wraps(fun)
    def living(*args, **kw):
        ...
    return func

这里有个简单的演示来证明这个观点:

>>> def wrapper(f):
    def func(*args):
        """The wrapper func's docstring."""
        return f(*args)
    return func

>>> @wrapper
def test(x):
    """The test func's docstring."""
    return x ** 2

>>> test.__doc__
"The wrapper func's docstring."

对比一下

>>> from functools import wraps
>>> def wrapper(f):
    @wraps(f)
    def func(*args):
        """The wrapper func's docstring."""
        return f(*args)
    return func

>>> @wrapper
def test(x):
    """The test func's docstring."""
    return x ** 2

>>> test.__doc__
"The test func's docstring."

撰写回答