Decorators are a powerful feature used to modify or extend functions or methods without changing their source code directly. They are functions themselves that take another function as an argument, add some functionality, and then return another function. Decorators allow you to wrap another function to modify its behavior.
Function Basics: Functions are first-class citizens, which means they can be passed around and used as arguments just like any other object (e.g., integers, strings).
Syntax: Decorators use the @decorator_name syntax above the function definition. It’s a cleaner and more readable way to apply decorators compared to the traditional way of using function_name = decorator_name(function_name).
Purpose: Common uses of decorators include logging, timing functions, access control, and memoization (caching results for performance).
We can create a custom decorator to measure the execution time of a function using time module:
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Execution of '{func.__name__}' took {end_time - start_time} seconds")
return result
return wrapper
Outer Function (measure_time):
func) as an argument.wrapper) that:
start_time) before calling func.func with its arguments (*args, **kwargs) and captures the result.end_time) after func completes.func.Inner Function (wrapper):
func) and calculates the time it takes to execute.Returning wrapper:
wrapper function, which replaces the original function when used as a decorator.@measure_time
def some_function():
time.sleep(2) # Simulate some work
print("Function executed")
some_function()
Function executed
Execution of 'some_function' took 2.0006470680236816 seconds
wrapper function uses *args and **kwargs to accept any number of positional and keyword arguments that func might take.Decorating Functions with Parameters: If the decorated function (func) takes parameters, the decorator (measure_time) should handle them correctly within wrapper.
Preserving Function Metadata: To preserve metadata (like __name__, __doc__, etc.) of the original function, you can use functools.wraps from the functools module:
from functools import wraps
def measure_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Implementation remains the same
pass
return wrapper