3.3 KiB
3.3 KiB
08 – Decorators in Python
This document explains decorators, how they work, and how they are used to extend function behavior without modifying the original function code.
1. What Is a Decorator?
A decorator is a function that:
- Takes another function as input
- Adds extra behavior
- Returns a new function
Decorators are commonly used for:
- Input validation
- Logging
- Authentication
- Performance measurement
- Access control
2. Basic Decorator Structure
A decorator has three layers:
- The decorator function
- The wrapper function
- The original function
General Pattern
def decorator(func):
def wrapper(*args, **kwargs):
# extra behavior
return func(*args, **kwargs)
return wrapper
3. Example: Input Validation Decorator
Code
def check_number(func):
def wrapper(a, b):
if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
print("Input must be numbers")
return
return func(a, b)
return wrapper
Explanation
check_numberis the decorator.funcis the function being decorated.wrapperreplaces the original function.aandbare the arguments passed to the original function.isinstance(a, (int, float))ensures inputs are numeric.- If validation fails, execution stops.
- If validation passes, the original function is called.
4. Using the Decorator with @ Syntax
Code
@check_number
def bemola(a, b):
try:
res = a / b
print(res)
except ZeroDivisionError:
print("Zero Number Detected")
except Exception as e:
print(f"Error Detected {e}")
What Happens Internally
This line:
@check_number
Is equivalent to:
bemola = check_number(bemola)
The function bemola is replaced by wrapper.
5. Execution Flow
When calling:
bemola(10, 2)
The flow is:
wrapper(10, 2)is called- Inputs are validated
func(10, 2)is executed- Result is printed
If calling:
bemola(10, "a")
The output will be:
Input must be numbers
6. Why Use Decorators?
Without decorators, input validation would need to be repeated in every function.
Decorators allow:
- Reusable logic
- Cleaner code
- Separation of concerns
7. Limitations in This Example
- The decorator only works with exactly two arguments.
- It does not preserve the original function’s metadata (
__name__,__doc__).
8. Improved Version (Best Practice)
from functools import wraps
def check_number(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not all(isinstance(x, (int, float)) for x in args):
print("Input must be numbers")
return
return func(*args, **kwargs)
return wrapper
Improvements
- Supports any number of arguments
- Preserves function name and documentation
- More reusable and professional
Summary
- Decorators modify function behavior without changing its code
- They wrap functions inside another function
@decoratoris syntactic sugar- Commonly used for validation, logging, and access control
- Best practice is to use
*args,**kwargs, andfunctools.wraps