Function Decorators in Python
Quick Answer
Function Decorators explains function decorators are a powerful feature in Python that allow you to modify the behavior of functions or methods.
Learning Objectives
- Explain the purpose of Function Decorators in a practical learning context.
- Identify the main ideas, terms, and decisions involved in Function Decorators.
- Apply Function Decorators in a simple real-world scenario or practice task.
Introduction
Function decorators are a powerful feature in Python that allow you to modify the behavior of functions or methods.
They provide a clean and readable way to extend functionality without changing the original function's code.
Decorators provide a simple syntax for calling higher-order functions.
What Are Function Decorators?
A function decorator is a function that takes another function as an argument and returns a new function with added functionality.
Decorators are often used to add logging, access control, memoization, or timing to existing functions.
- They wrap the original function to extend or alter its behavior.
- Use the '@' symbol to apply decorators in Python.
- They help keep code DRY (Don't Repeat Yourself) by reusing common functionality.
How to Define and Use a Simple Decorator
To create a decorator, define a function that accepts a function as an argument and returns a new function.
Use the '@decorator_name' syntax above the function you want to decorate.
Example: A Simple Logging Decorator
This decorator prints a message before and after the execution of the decorated function.
Decorators with Arguments
Sometimes decorators need to accept their own arguments. To achieve this, you create a decorator factory that returns a decorator.
This adds an extra layer of functions to handle the arguments.
- Decorator factory: a function that returns a decorator.
- The returned decorator then wraps the target function.
Common Use Cases for Decorators
Decorators are widely used in Python frameworks and libraries for various purposes.
- Logging function calls and arguments.
- Measuring execution time.
- Access control and authentication.
- Memoization or caching results.
- Retrying operations on failure.
Understanding functools.wraps
When you create decorators, the original function’s metadata like its name and docstring get lost.
The functools.wraps decorator helps preserve this metadata.
- Use @functools.wraps on the inner wrapper function.
- It copies the original function’s __name__, __doc__, and other attributes.
Practical Example
This example defines a decorator that logs messages before and after calling the greet function.
This decorator repeats the execution of the decorated function a specified number of times.
Using functools.wraps preserves the original function’s metadata when decorating.
Examples
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
result = func(*args, **kwargs)
print(f"{func.__name__} finished.")
return result
return wrapper
@log_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")This example defines a decorator that logs messages before and after calling the greet function.
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(times=3)
def say_hello():
print("Hello!")
say_hello()This decorator repeats the execution of the decorated function a specified number of times.
import functools
def log_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
return func(*args, **kwargs)
return wrapperUsing functools.wraps preserves the original function’s metadata when decorating.
Best Practices
- Always use functools.wraps to preserve function metadata.
- Keep decorators simple and focused on a single responsibility.
- Use decorators to improve code readability and reuse.
- Test decorated functions to ensure behavior is as expected.
- Avoid complex nested decorators unless necessary.
Common Mistakes
- Not using functools.wraps, which causes loss of function metadata.
- Writing decorators that do not accept arbitrary arguments (*args, **kwargs).
- Modifying the original function instead of returning a new wrapper.
- Applying decorators incorrectly without the '@' syntax.
- Creating decorators that are hard to debug due to unclear wrapper logic.
Hands-on Exercise
Create a Timing Decorator
Write a decorator that measures and prints the execution time of a function.
Expected output: Printed execution time in seconds after the function runs.
Hint: Use the time module and record time before and after the function call.
Implement a Memoization Decorator
Create a decorator that caches the results of a function to avoid repeated calculations.
Expected output: Function returns cached results on repeated calls with the same arguments.
Hint: Use a dictionary to store results keyed by function arguments.
Interview Questions
What is a function decorator in Python?
InterviewA function decorator is a callable that takes a function as an argument and returns a new function that extends or modifies the behavior of the original function.
Why should you use functools.wraps in a decorator?
Interviewfunctools.wraps preserves the original function’s metadata such as its name and docstring, which would otherwise be lost when wrapping the function.
How do you create a decorator that accepts arguments?
InterviewYou create a decorator factory function that takes the decorator arguments and returns the actual decorator function.
MCQ Quiz
1. What is the primary purpose of a function decorator in Python?
Select one option to check your answer.
2. How do you apply a decorator named 'log_decorator' to a function 'greet' in Python?
Select one option to check your answer.
3. What is the role of functools.wraps in creating decorators?
Select one option to check your answer.
4. Which of the following is a correct way to create a decorator that accepts arguments?
Select one option to check your answer.
5. What common mistake can cause loss of the original function's metadata when using decorators?
Select one option to check your answer.
Key Takeaways
- Function decorators are a powerful feature in Python that allow you to modify the behavior of functions or methods.
- They provide a clean and readable way to extend functionality without changing the original function's code.
- A function decorator is a function that takes another function as an argument and returns a new function with added functionality.
- Decorators are often used to add logging, access control, memoization, or timing to existing functions.
- To create a decorator, define a function that accepts a function as an argument and returns a new function.
Frequently Asked Questions
Can decorators be applied to classes?
Yes, decorators can be applied to classes to modify or extend their behavior, similar to how they work with functions.
What is the difference between a decorator and a higher-order function?
A decorator is a specific type of higher-order function designed to modify or enhance other functions or methods.
Are decorators executed at runtime or compile time?
Decorators are executed at the time the function is defined, which is typically during module loading.
Summary
Function decorators in Python provide a flexible way to modify or extend the behavior of functions without changing their code.
They are widely used for logging, timing, access control, and caching among other use cases.
Using functools.wraps is essential to preserve function metadata when creating decorators.
Understanding decorators will help you write cleaner, more reusable, and maintainable Python code.





