Decorators
Understanding Decorators with a Detailed Example¶
Scenario:¶
Let's say we want to log whenever a function is called. Instead of adding logging code inside every function, we can use a decorator.
Step 1: Creating a Decorator Function¶
A decorator is a function that takes another function as an argument, wraps it in additional functionality, and returns the modified function.
import time
def logger_decorator(func):
def wrapper(*args, **kwargs):
print(f"[LOG] Function '{func.__name__}' is about to be called with arguments: {args}, {kwargs}")
start_time = time.time() # Start timing the function execution
result = func(*args, **kwargs) # Call the original function
end_time = time.time() # End timing
print(f"[LOG] Function '{func.__name__}' executed in {end_time - start_time:.4f} seconds")
print(f"[LOG] Function '{func.__name__}' returned: {result}\n")
return result # Return the original function's result
return wrapper
Step 2: Applying the Decorator¶
Now, we can use @logger_decorator to log function calls.
@logger_decorator
def add(a, b):
time.sleep(1) # Simulating a delay
return a + b
@logger_decorator
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# Calling the functions
add(5, 3)
greet("Alice")
greet("Bob", greeting="Hi")
Step 3: Understanding the Output¶
When we run the above code, we get:
[LOG] Function 'add' is about to be called with arguments: (5, 3), {}
[LOG] Function 'add' executed in 1.0003 seconds
[LOG] Function 'add' returned: 8
[LOG] Function 'greet' is about to be called with arguments: ('Alice',), {}
[LOG] Function 'greet' executed in 0.0000 seconds
[LOG] Function 'greet' returned: Hello, Alice!
[LOG] Function 'greet' is about to be called with arguments: ('Bob',), {'greeting': 'Hi'}
[LOG] Function 'greet' executed in 0.0000 seconds
[LOG] Function 'greet' returned: Hi, Bob!
Step 4: Explanation¶
1. Decorator (logger_decorator
)¶
- Takes a function
func
as input. - Defines an inner
wrapper
function that:- Logs the function call.
- Measures execution time.
- Calls the original function.
- Logs the return value.
- Returns the function’s result.
- Returns the
wrapper
function.
2. Applying the Decorator¶
- The
@logger_decorator
syntax applies the decorator toadd()
andgreet()
. - Instead of calling
add()
directly, Python callswrapper()
, which logs details and executesadd()
.
3. Flexible Arguments (*args, **kwargs
)¶
- Allows the decorator to work with any function, no matter its parameters.
Why Use Decorators?¶
✅ Code Reusability – You don’t need to modify every function manually.
✅ Separation of Concerns – The function logic remains clean, and logging/debugging is handled separately.
✅ Scalability – You can add more decorators (e.g., caching, authentication) without modifying the core function.