Files
python-doc/Docs/Basic/08-decorators.md
2026-02-04 21:23:37 +03:30

183 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 functions 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`