patternpythonCriticalCanonical
What does functools.wraps do?
Viewed 0 times
wrapsdoeswhatfunctools
Problem
In a comment on this answer to another question, someone said that they weren't sure what
functools.wraps was doing. So, I'm asking this question so that there will be a record of it on StackOverflow for future reference: what does functools.wraps do, exactly?Solution
When you use a decorator, you're replacing one function with another. In other words, if you have a decorator
then when you say
it's exactly the same as saying
and your function
it will print
If using a decorator always meant losing this information about a function, it would be a serious problem. That's why we have
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_loggingthen when you say
@logged
def f(x):
"""does some math"""
return x + x * xit's exactly the same as saying
def f(x):
"""does some math"""
return x + x * x
f = logged(f)and your function
f is replaced with the function with_logging. Unfortunately, this means that if you then sayprint(f.__name__)it will print
with_logging because that's the name of your new function. In fact, if you look at the docstring for f, it will be blank because with_logging has no docstring, and so the docstring you wrote won't be there anymore. Also, if you look at the pydoc result for that function, it won't be listed as taking one argument x; instead it'll be listed as taking *args and **kwargs because that's what with_logging takes.If using a decorator always meant losing this information about a function, it would be a serious problem. That's why we have
functools.wraps. This takes a function used in a decorator and adds the functionality of copying over the function name, docstring, arguments list, etc. And since wraps is itself a decorator, the following code does the correct thing:from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'Code Snippets
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging@logged
def f(x):
"""does some math"""
return x + x * xdef f(x):
"""does some math"""
return x + x * x
f = logged(f)print(f.__name__)from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'Context
Stack Overflow Q#308999, score: 1699
Revisions (0)
No revisions yet.