Question

def decor(fun):
    def living(*args, **kw):
        return fun(*args, **kw)
    return living

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

print test.__doc__

why the result is None? Something happened when I use decorator? Thanks to answer!

Was it helpful?

Solution

Because when you wrap a function in a decorator:

@decor
def test:

you get back the function created by the decorator, (living, in this case) which doesn't have the same docstring, etc. It doesn't "lose" this data, living never had it!

You can get around this with functools.wraps:

from functools import wraps

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

A quick demo to prove the point:

>>> 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."

versus

>>> 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."

OTHER TIPS

That's because your decorator is basically replacing your function. You need to use functools.wraps() to store the decorated functions internals like __name__ and __doc__.

You can test this easily by adding a docstring to your decorator function 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()'

Example from the docs of functools.wraps() which saves the wrapped functions name and docstring.

>>> 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'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top