How to Use functools.wraps in Python: Simple Guide
Use
functools.wraps as a decorator inside your custom decorator to copy the original function's name, docstring, and other metadata. This keeps the wrapped function's identity intact, which helps with debugging and documentation.Syntax
The functools.wraps decorator is used inside your decorator function to wrap the original function. It takes the original function as an argument and returns a decorator that updates the wrapper function's metadata.
Basic syntax:
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# code before
result = func(*args, **kwargs)
# code after
return result
return wrapperpython
from functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # code before result = func(*args, **kwargs) # code after return result return wrapper
Example
This example shows a decorator that prints a message before and after calling the original function. Using @wraps(func) keeps the original function's name and docstring.
python
from functools import wraps def announce(func): @wraps(func) def wrapper(*args, **kwargs): print(f"Calling {func.__name__}...") result = func(*args, **kwargs) print(f"Finished {func.__name__}.") return result return wrapper @announce def greet(name): """Greet someone by name.""" print(f"Hello, {name}!") print(greet.__name__) print(greet.__doc__) greet("Alice")
Output
greet
Greet someone by name.
Calling greet...
Hello, Alice!
Finished greet.
Common Pitfalls
Without using functools.wraps, the wrapper function replaces the original function's metadata. This causes issues like losing the original function's name and docstring, which can confuse debugging and documentation tools.
Wrong way (missing @wraps):
python
def decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @decorator def example(): """Original docstring.""" pass print(example.__name__) # Outputs 'wrapper' print(example.__doc__) # Outputs None # Correct way with wraps: from functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @decorator def example(): """Original docstring.""" pass print(example.__name__) # Outputs 'example' print(example.__doc__) # Outputs 'Original docstring.'
Output
wrapper
None
example
Original docstring.
Quick Reference
- Purpose: Preserve original function metadata in decorators.
- Import:
from functools import wraps - Usage: Place
@wraps(original_function)above the wrapper function inside your decorator. - Benefits: Keeps function name, docstring, annotations, and module info intact.
Key Takeaways
Always use functools.wraps inside decorators to keep the original function's metadata.
Without wraps, the wrapper function hides the original function's name and docstring.
Place @wraps(func) directly above the wrapper function inside your decorator.
Using wraps helps debugging, documentation, and tools that rely on function metadata.