Введение
Декораторы Python - это мощный инструмент, который позволяет изменять поведение функций без изменения их исходного кода. В этом руководстве мы рассмотрим, как использовать несколько декораторов для одной функции Python, открывая мир возможностей для повторного использования кода и улучшения функций.
Понимание декораторов Python
Что такое декораторы в Python?
Декораторы в Python - это мощный и гибкий способ изменить поведение функции или класса без изменения их исходного кода. Это способ "обернуть" функцию другой функцией, позволяя обёртке выполнять код до и/или после вызова исходной функции.
Зачем использовать декораторы?
Декораторы полезны для различных задач, таких как:
- Логирование вызовов функций
- Кэширование результатов функций
- Применение контроля доступа
- Измерение времени выполнения функции
- Повторение неудачных вызовов функций
Как работают декораторы?
Декораторы в Python определяются с использованием символа @, за которым следует функция-декоратор. Функция-декоратор принимает функцию в качестве аргумента, выполняет дополнительную обработку и затем возвращает новую функцию, которую можно вызвать вместо исходной.
Вот простой пример функции-декоратора, которая логирует аргументы, передаваемые в функцию:
def log_args(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args} and kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log_args
def add_numbers(a, b):
return a + b
result = add_numbers(2, 3)
print(result)
Это выведет:
Calling add_numbers with args=(2, 3) and kwargs={}
5
Вложенные декораторы
Декораторы могут быть вложенными, что позволяет применить несколько декораторов к одной функции. Порядок применения декораторов важен, так как он определяет порядок выполнения функций-декораторов.
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def reverse(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result[::-1]
return wrapper
@uppercase
@reverse
def greet(name):
return f"Hello, {name}!"
print(greet("LabEx"))
Это выведет:
!XEbal ,OLLEH
Аргументы декоратора
Декораторы также могут принимать аргументы, что позволяет настроить поведение декоратора. Это полезно, когда вы хотите создать декоратор, который можно настроить различными способами.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
result = ""
for _ in range(n):
result += func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!"
print(greet("LabEx"))
Это выведет:
Hello, LabEx!Hello, LabEx!Hello, LabEx!
Применение нескольких декораторов
Порядок применения декораторов
Когда вы применяете несколько декораторов к функции, порядок их применения важен. Декораторы применяются снизу вверх, то есть самый внутренний декоратор применяется первым, а самый внешний - последним.
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def reverse(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result[::-1]
return wrapper
@uppercase
@reverse
def greet(name):
return f"Hello, {name}!"
print(greet("LabEx"))
Это выведет:
!XEBAL,OLLEH
Слоение декораторов
Вы также можете накладывать несколько декораторов на одну функцию, применяя их последовательно. Это эквивалентно вложению декораторов, но может сделать код более читаемым.
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def reverse(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result[::-1]
return wrapper
def greet(name):
return f"Hello, {name}!"
greet_upper_reverse = uppercase(reverse(greet))
print(greet_upper_reverse("LabEx"))
Это выведет:
!XEBAL,OLLEH
Декорирование методов
Декораторы также можно использовать для изменения поведения методов в классе. Применяются те же принципы, но функция-декоратор должна принимать параметр self в качестве первого аргумента.
def log_method(func):
def wrapper(self, *args, **kwargs):
print(f"Calling {func.__name__} on {self.__class__.__name__} with args={args} and kwargs={kwargs}")
return func(self, *args, **kwargs)
return wrapper
class Person:
def __init__(self, name):
self.name = name
@log_method
def greet(self, message):
return f"{message}, {self.name}!"
person = Person("LabEx")
print(person.greet("Hello"))
Это выведет:
Calling greet on Person with args=('Hello',) and kwargs={}
Hello, LabEx!
Декорирование классов
Декораторы также можно использовать для изменения поведения целых классов. В этом случае функция-декоратор принимает класс в качестве аргумента и возвращает новый класс с желаемым поведением.
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class MyClass:
def __init__(self, value):
self.value = value
obj1 = MyClass(42)
obj2 = MyClass(24)
print(obj1 is obj2) ## True
print(obj1.value) ## 42
print(obj2.value) ## 42
В этом примере декоратор singleton гарантирует, что создается только один экземпляр класса MyClass, независимо от того, сколько раз класс инстанцируется.
Примеры использования декораторов в реальных сценариях
Декоратор для логирования
Одним из распространенных применений декораторов является логирование вызовов функций. Это может быть полезно для отладки, мониторинга или аудита.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args} and kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log_function_call
def add_numbers(a, b):
return a + b
result = add_numbers(2, 3)
print(result)
Это выведет:
Calling add_numbers with args=(2, 3) and kwargs={}
5
Декоратор для кэширования
Декораторы также можно использовать для кэширования результатов ресурсоемких вызовов функций, что улучшает производительность.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
else:
return (fibonacci(n-1) + fibonacci(n-2))
print(fibonacci(100))
Декоратор lru_cache из модуля functools предоставляет простой способ реализации кэша с политикой Least Recently Used (LRU) для результатов функций.
Декоратор для контроля доступа
Декораторы можно использовать для применения контроля доступа к функциям или методам, чтобы гарантировать, что только авторизованные пользователи могут использовать определенную функциональность.
from functools import wraps
def require_admin(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not is_admin(args[0]):
raise ValueError("Access denied. You must be an admin.")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, name, is_admin):
self.name = name
self.is_admin = is_admin
@require_admin
def delete_user(self, user_to_delete):
print(f"Deleting user: {user_to_delete.name}")
admin = User("LabEx", True)
regular_user = User("John", False)
admin.delete_user(regular_user) ## Works
regular_user.delete_user(admin) ## Raises ValueError
В этом примере декоратор require_admin проверяет, является ли пользователь, вызывающий метод delete_user, администратором, прежде чем разрешить выполнение операции.
Декоратор для повторных попыток
Декораторы также можно использовать для реализации механизма повторных попыток для функций, которые могут завершиться неудачно из-за временных проблем, таких как сетевые ошибки или ограничения по количеству запросов к API.
import time
from functools import wraps
def retry(max_retries=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Function {func.__name__} failed. Retrying... ({retries+1}/{max_retries})")
retries += 1
time.sleep(delay)
raise Exception(f"Maximum number of retries ({max_retries}) reached for function {func.__name__}")
return wrapper
return decorator
@retry(max_retries=3, delay=2)
def flaky_function():
## Simulate a flaky function that fails 50% of the time
if random.random() < 0.5:
raise Exception("Oops, something went wrong!")
return "Success!"
print(flaky_function())
В этом примере декоратор retry автоматически повторит вызов функции flaky_function до 3 раз с задержкой в 2 секунды между каждой попыткой, прежде чем выбросить исключение.
Это лишь несколько примеров из множества реальных сценариев использования декораторов в Python. Декораторы - это мощный и гибкий инструмент, который может помочь вам писать более модульный, поддерживаемый и повторно используемый код.
Резюме
По завершении этого руководства у вас будет твердое понимание того, как применять несколько декораторов к одной функции Python. Вы изучите практические примеры и реальные сценарии использования, что позволит вам писать более модульный, гибкий и поддерживаемый код на Python.



