decorators doc
This commit is contained in:
182
Docs/Basic/08-decorators.md
Normal file
182
Docs/Basic/08-decorators.md
Normal file
@@ -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`
|
||||||
Reference in New Issue
Block a user