Руководство по декораторам Python для продвинутых программистов

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

Кто такие декораторы?

Декораторы — это функции, которые изменяют поведение другой функции. Они оборачивают другую функцию, чтобы расширить ее поведение без явного изменения ее кода. Декораторы определяются с помощью синтаксиса @decorator_name и размещаются над определением функции.

Базовый синтаксис декоратора

Простой декоратор принимает функцию в качестве аргумента, определяет внутреннюю функцию, которая добавляет некоторое поведение, а затем возвращает внутреннюю функцию.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

Функции декоратора с аргументами

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

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Декораторы для вложений

Декораторы могут быть вложенными для объединения нескольких поведений. Например, мы можем использовать два или более декораторов для одной функции.

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def repeat_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + result
    return wrapper

@repeat_decorator
@uppercase_decorator
def say_word(word):
    return word

print(say_word("hello"))

Декораторы на основе классов

В Python декораторы также могут быть реализованы как классы с помощью метода __call__. Декораторы на основе классов полезны, когда вам нужно более сложное управление состоянием и поведением.

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()

Использование functools.wraps для сохранения метаданных

Когда вы пишете декораторы, декорированная функция теряет свои исходные метаданные, такие как имя и docstring. Декоратор functools.wraps можно использовать для копирования метаданных исходной функции в функцию-обертку.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Wrapper function executed before", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def display_info(name, age):
    """Displays name and age."""
    print(f"display_info ran with arguments ({name}, {age})")

print(display_info.__name__)  # Output: display_info
print(display_info.__doc__)   # Output: Displays name and age.

Заключение

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

Ссылки
Python