[Tutor] Help with update_wrapper

Peter Otten __peter__ at web.de
Sat Dec 10 10:56:32 CET 2011


Emeka wrote:

> Could someone explain " functools.update_wrapper" with simple examples?

Since this is not for the absolute beginner I'm assuming you are already 
familiar with decorators. In their most common form these are functions that 
take a function and wrap that function into another function.

@deco
def f(...):
    ...

is only syntactic sugar for

def f(...):
    ...
f = deco(f)

i. e. you can get a decorated version of f on the fly with deco(f).

Suppose you have a function add() that adds two arguments and a decorator 
log_call() that prints the name of the called function before calling it:

>>> import pydoc
>>> pydoc.pager = pydoc.plainpager # this lets you see the output of help()
>>> from functools import update_wrapper
>>> def add(x, y):
...     "calculate x+y"
...     return x + y
...
>>> def log_call(f):
...     def g(*args, **kw):
...             print "calling", f.__name__
...             return f(*args, **kw)
...     return g
...
>>> add(1, 2)
3
>>> log_call(add)(3, 4)
calling add
7

It works, but if you want to learn more about the decorated function

>>> help(log_call(add))
Help on function g in module __main__:

g(*args, **kw)

you get the name, the signature and docstring of the wrapping function g(), 
i. e. by decorating it you lose valuable information about add(). If you 
decorate a mul() function

>>> @log_call
... def mul(x, y):
...     "multiply x and y"
...     return x * y
...
>>> help(mul)
Help on function g in module __main__:

g(*args, **kw)

the help will be exactly the same. 
functools.update_wrapper() is a partial fix for the problem:

>>> add2 = update_wrapper(log_call(add), add)
>>> add2(5, 6)
calling add
11
>>> help(add2)
Help on function add in module __main__:

add(*args, **kw)
    calculate x+y

It copies name and docstring (but not the function signature).
However, I don't think I will ever use it directly, I'd prefer using 
functools.wraps:

>>> def log_call2(f):
...     @wraps(f)
...     def g(*args, **kw):
...             print "calling", f.__name__
...             return f(*args, **kw)
...     return g
...
>>> @log_call2
... def add(x, y):
...     "yadda"
...     return x + y
...
>>> add(1, 2)
calling add
3
>>> help(add)
Help on function add in module __main__:

add(*args, **kw)
    yadda

If you are seriously interested in this you should also have a look at 
Michele Simionato's decorator module (http://pypi.python.org/pypi/decorator) 
that also fixes the signature:

>>> import decorator
>>> @decorator.decorator
... def log_call(f, *args, **kw):
...     print "calling", f.__name__
...     return f(*args, **kw)
...
>>> @log_call
... def add(x, y):
...     "calculate x+y"
...     return x + y
...
>>> add(1, 2)
calling add
3
>>> help(add)
Help on function add in module __main__:

add(x, y)
    calculate x+y







More information about the Tutor mailing list