Декоратори
Декоратори — одна з найелегантніших і найпотужніших особливостей Python. Вони дозволяють розробникам змінювати поведінку функції або методу без зміни їх коду. Якщо ви коли-небудь використовували Python-фреймворки, такі як Flask чи Django, то, ймовірно, вже використовували декоратори, навіть не усвідомлюючи цього.
У цьому блозі ми детально розглянемо концепцію декораторів у Python, пояснимо її через чіткі приклади та варіанти використання. Наприкінці ви не тільки зрозумієте, як працюють декоратори, але й дізнаєтесь, як написати власні.
Що таке декоратори?
Декоратор — це функція, яка приймає іншу функцію (або метод) як аргумент і розширює чи змінює її поведінку без явного модифікування. Уявіть це як обгортку, яка додає функціональність до існуючого коду.
Базовий синтаксис
@decorator_function
def target_function():
pass
Синтаксис @decorator_function
є скороченням для:
def target_function():
pass
target_function = decorator_function(target_function)
Як працюють декоратори
Декоратор — це, по суті, функція вищого порядку. Вона приймає функцію як вхідний параметр і повертає нову функцію. Давайте побудуємо один крок за кроком.
Приклад 1: Простіший декоратор
def my_decorator(func):
def wrapper():
print("Щось відбувається до виклику функції.")
func()
print("Щось відбувається після виклику функції.")
return wrapper
@my_decorator
def say_hello():
print("Привіт, світ!")
say_hello()
Виведення:
Щось відбувається до виклику функції.
Привіт, світ!
Щось відбувається після виклику функції.
Тут, my_decorator
обгортає функцію say_hello
і додає поведінку до та після її виконання.
Варіанти використання декораторів
1. Логування
Декоратори часто використовуються для логування викликів функцій та їхніх результатів.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Функція '{func.__name__}' була викликана з аргументами {args} та ключовими аргументами {kwargs}.")
result = func(*args, **kwargs)
print(f"Функція '{func.__name__}' повернула: {result}")
return result
return wrapper
@log_function_call
def add(a, b):
return a + b
add(5, 3)
Виведення:
Функція 'add' була викликана з аргументами (5, 3) та ключовими аргументами {}.
Функція 'add' повернула: 8
2. Вимірювання часу виконання
Можна виміряти час виконання функції за допомогою декораторів.
import time
def timing_function(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Функція '{func.__name__}' виконувалась {end_time - start_time:.4f} секунд.")
return result
return wrapper
@timing_function
def compute_square(numbers):
return [n ** 2 for n in numbers]
compute_square(range(1, 100000))
**Виведення:**
Функція 'compute_square' виконувалась 0.0102 секунд.
3. Аутентифікація
Декоратори можуть забезпечити аутентифікацію користувачів або контроль доступу.
def authenticate_user(func):
def wrapper(user, *args, **kwargs):
if not user.get("is_authenticated"):
print("Користувач не аутентифікований!")
return
return func(user, *args, **kwargs)
return wrapper
@authenticate_user
def view_dashboard(user):
print(f"Ласкаво просимо {user['username']} до панелі керування!")
user1 = {"username": "John", "is_authenticated": True}
user2 = {"username": "Doe", "is_authenticated": False}
view_dashboard(user1)
view_dashboard(user2)
Виведення:
Ласкаво просимо John до панелі керування!
Користувач не аутентифікований!
4.
Логіка повторних спроб
Автоматично повторювати виклик функції, якщо вона не вдалася.
import random
def retry_function(retries=3):
def decorator(func):
def wrapper(*args, **kwargs):
attempts = 0
while attempts < retries:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
print(f"Спроба {attempts} не вдалася: {e}")
print("Усі спроби не вдалися.")
return wrapper
return decorator
@retry_function(retries=5)
def flaky_function():
if random.random() < 0.7:
raise ValueError("Випадкова помилка!")
print("Функція виконана успішно!")
flaky_function()
**Виведення:**
Спроба 1 не вдалася: Випадкова помилка!
Спроба 2 не вдалася: Випадкова помилка!
Функція виконана успішно!
Розширені декоратори
1. Декоратори з аргументами
Можна створювати декоратори, які самі приймають аргументи.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Привіт, {name}!")
greet("Alice")
Виведення:
Привіт, Alice!
Привіт, Alice!
Привіт, Alice!
2. Ланцюгування декораторів
До однієї функції можна застосувати кілька декораторів.
def uppercase(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs).upper()
return wrapper
def exclaim(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) + "!"
return wrapper
@uppercase
@exclaim
def greet():
return "hello"
print(greet())
Виведення:
HELLO!
Висновок
Декоратори Python — це потужний інструмент для розширення функціональності, покращення модульності та підвищення повторного використання коду. Від логування та вимірювання часу до аутентифікації та логіки повторних спроб — декоратори пропонують безліч можливостей для спрощення вашого коду. Оволодіння декораторами значно підвищить ваші навички розробки на Python.
Автор: СіваСантуш Вемпалі
Інженер-програміст з 4+ роками досвіду
Пристрасний до Python, розробки бекенду та масштабованих систем.
Перекладено з: Mastering Python Decorators: Simplify Your Code with This Powerful Tool