functools.wraps
- Python course
- Intermediate
- Lesson 40/116
- 4 min
Preserve decorator metadata with functools.wraps.
The wrapper loses identity
When a decorator wraps a target, metadata like __name__ and __doc__ moves to the wrapper. So hello.__name__ is no longer "hello" but "wrapper". This breaks debugging and help output.
The fix: write @functools.wraps(target) at the top of the wrapper. It copies the target's name, docstring and other metadata to the wrapper. From the outside the wrapper looks like the target.
What does wraps copy?
@functools.wraps(fn) copies not just the name but also attributes like __doc__, __module__, __qualname__ and __dict__. So help(hello) shows the real hello docstring, not the wrapper's.
Signature information also looks more accurate. This matters when writing libraries; users must know which function they call. Using wraps when writing decorators is a good habit.
Practice A decorator that uses wraps
Start with import functools. Define a decorator named call; signature def call(fn):. Inside, write @functools.wraps(fn) on the line above def wrapper(*args, **kwargs):. The wrapper body should be return fn(*args, **kwargs). End with return wrapper. Decorate def hello(): with @call; body returns "Hi!". Finally call print(hello.__name__).
Starter code
# Write the call decorator and the hello functionSample solution
# Write the call decorator and the hello function
import functools
def call(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return wrapper
@call
def hello():
return "Hi!"
print(hello.__name__)Expected output
hello
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What does @functools.wraps prevent from being lost?
- The parameters
- The function's return value
- The function name and docstring Correct answer
- Error raising
Explanation: @functools.wraps prevents the target's name and docstring from moving to the wrapper.
2 Complete the wraps line
def call(fn):
@____(fn)
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return wrapper
def call(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return wrapper
Explanation: functools.wraps is written at the top of the wrapper.
3 Match wraps and metadata
| Term | Meaning |
|---|---|
__name__ |
Function name |
__doc__ |
Docstring |
__module__ |
Defining module |
@functools.wraps |
Copies metadata |