Python tutorials > Advanced Python Concepts > Decorators > What is `@wraps`?
What is `@wraps`?
The @wraps
decorator is part of the functools
module in Python. It's a crucial tool when working with decorators because it helps preserve the original function's metadata (like its name, docstring, and signature) when it's wrapped by a decorator. Without @wraps
, the decorated function would appear to have the metadata of the wrapper function, making debugging and introspection more difficult.
Basic Example Without @wraps
In this example, we define a simple decorator my_decorator
that wraps the say_hello
function. When we print say_hello.__name__
and say_hello.__doc__
, we see that the name and docstring are those of the wrapper
function, not the original say_hello
function. This is undesirable because it obscures the true identity of the function being decorated.
def my_decorator(func):
def wrapper(*args, **kwargs):
"""This is the wrapper function's docstring."""
print("Before calling the function.")
result = func(*args, **kwargs)
print("After calling the function.")
return result
return wrapper
@my_decorator
def say_hello(name):
"""Says hello to the given name."""
return f"Hello, {name}!"
print(say_hello.__name__)
print(say_hello.__doc__)
Using @wraps
By adding @wraps(func)
to the wrapper
function definition, we instruct the decorator to update the wrapper
function's attributes to match those of the original func
. Now, when we print say_hello.__name__
and say_hello.__doc__
, we see the original function's name and docstring (say_hello
and "Says hello to the given name."), as intended.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""This is the wrapper function's docstring."""
print("Before calling the function.")
result = func(*args, **kwargs)
print("After calling the function.")
return result
return wrapper
@my_decorator
def say_hello(name):
"""Says hello to the given name."""
return f"Hello, {name}!"
print(say_hello.__name__)
print(say_hello.__doc__)
Concepts Behind the Snippet
The core concept behind @wraps
is preserving function metadata during decoration. Decorators inherently replace a function with another (the wrapper). Without @wraps
, the new function's metadata overwrites the original's, hindering introspection and debugging. @wraps
copies attributes like __name__
, __doc__
, and __annotations__
from the original function to the wrapper, maintaining the function's identity.
Real-Life Use Case Section
Consider a framework for creating web application routes. Each route handler (function) might be decorated with a custom decorator to handle authentication or authorization. Without @wraps
, the route handlers would all appear to have the same name and docstring as the decorator's wrapper, making it impossible to distinguish them in logging or debugging. Using @wraps
ensures that each route handler retains its original name and docstring, enabling developers to easily identify and manage individual routes.
Best Practices
@wraps
when writing decorators. It avoids unexpected behavior and makes your code easier to understand and maintain.@wraps
. This will help you debug issues if they arise.@wraps
handles standard attributes, you might have custom attributes on your functions that you want to preserve. You'll need to manually copy these in your decorator.
Interview Tip
When asked about decorators in Python, mentioning the importance of @wraps
demonstrates a deep understanding of the topic. Explain how it prevents metadata loss and why this is crucial for debugging and introspection. You can also mention scenarios where custom attributes need to be preserved manually in addition to using @wraps
.
When to Use Them
You should use @wraps
whenever you define a decorator. There's virtually no downside, and it prevents numerous potential headaches down the line. It is especially important when creating decorators intended for public APIs or libraries.
Memory Footprint
@wraps
has a negligible impact on memory footprint. It primarily copies attributes from one function object to another, which are relatively small operations. The overhead is insignificant compared to the overall memory usage of a typical Python application.
Alternatives
While @wraps
is the standard and recommended way to preserve function metadata, you could manually copy the attributes yourself. However, this is error-prone and unnecessary, as @wraps
handles this elegantly. There are no practical alternatives that provide the same functionality with better performance or readability.
Pros
Cons
There are essentially no significant cons to using @wraps
. It's a lightweight and highly beneficial tool for decorator development.
FAQ
-
What happens if I don't use
@wraps
in my decorator?
Without
@wraps
, the decorated function will have the name, docstring, and other attributes of the wrapper function, not the original function. This can make debugging and understanding your code much harder. -
Does
@wraps
copy all attributes from the original function?
@wraps
copies essential attributes like__name__
,__doc__
,__annotations__
, and__module__
. If you have custom attributes on your functions, you'll need to copy them manually. -
Is
@wraps
necessary for all types of decorators?
Yes, it's a best practice to use
@wraps
in all your decorators to ensure proper function metadata preservation, regardless of the complexity of the decorator.