Как использовать несколько декораторов для одной функции Python

PythonPythonBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Декораторы Python - это мощный инструмент, который позволяет изменять поведение функций без изменения их исходного кода. В этом руководстве мы рассмотрим, как использовать несколько декораторов для одной функции Python, открывая мир возможностей для повторного использования кода и улучшения функций.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/lambda_functions("Lambda Functions") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/function_definition -.-> lab-397700{{"Как использовать несколько декораторов для одной функции Python"}} python/arguments_return -.-> lab-397700{{"Как использовать несколько декораторов для одной функции Python"}} python/lambda_functions -.-> lab-397700{{"Как использовать несколько декораторов для одной функции Python"}} python/decorators -.-> lab-397700{{"Как использовать несколько декораторов для одной функции Python"}} python/context_managers -.-> lab-397700{{"Как использовать несколько декораторов для одной функции Python"}} end

Понимание декораторов 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.