diff --git a/Docs/Basic/08-decorators.md b/Docs/Basic/08-decorators.md new file mode 100644 index 0000000..12fb985 --- /dev/null +++ b/Docs/Basic/08-decorators.md @@ -0,0 +1,182 @@ +# 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: + +1. The decorator function +2. The wrapper function +3. The original function + +### General Pattern + +```python +def decorator(func): + def wrapper(*args, **kwargs): + # extra behavior + return func(*args, **kwargs) + return wrapper +``` + +--- + +## 3. Example: Input Validation Decorator + +### Code + +```python +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_number` is the decorator. +* `func` is the function being decorated. +* `wrapper` replaces the original function. +* `a` and `b` are 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 + +```python +@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: + +```python +@check_number +``` + +Is equivalent to: + +```python +bemola = check_number(bemola) +``` + +The function `bemola` is replaced by `wrapper`. + +--- + +## 5. Execution Flow + +When calling: + +```python +bemola(10, 2) +``` + +The flow is: + +1. `wrapper(10, 2)` is called +2. Inputs are validated +3. `func(10, 2)` is executed +4. Result is printed + +If calling: + +```python +bemola(10, "a") +``` + +The output will be: + +```text +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) + +```python +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 +* `@decorator` is syntactic sugar +* Commonly used for validation, logging, and access control +* Best practice is to use `*args`, `**kwargs`, and `functools.wraps`