Как с помощью декораторов можно добавлять метаданные к функциям?

Декораторы могут добавлять метаданные к функциям, привязывая атрибуты к объекту функции. Например, можно добавить атрибут description, author или version. Для сохранения исходной сигнатуры декорируемой функции важно использовать functools.wraps. Пример:
import functools

def add_metadata(description, author):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        wrapper.description = description
        wrapper.author = author
        return wrapper
    return decorator

@add_metadata(description="Функция для сложения двух чисел.", author="John Doe")
def add(x, y):
    return x + y

print(add.description)
print(add.author)
  

Декораторы в Python позволяют элегантно добавлять метаданные к функциям, не изменяя их основной код. Метаданные могут быть полезны для документирования, introsкции, инструментов анализа кода, и для различных других целей.

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

Пример:


import functools

def add_metadata(metadata):
    def decorator(func):
        @functools.wraps(func) # Важно для сохранения исходной информации о функции
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)

        for key, value in metadata.items():
            setattr(wrapper, key, value)  # Добавляем метаданные как атрибуты

        return wrapper
    return decorator

@add_metadata({'description': 'Эта функция выполняет сложение.', 'author': 'AI Assistant'})
def add(x, y):
    """Складывает два числа."""
    return x + y

print(add.description)  # Выведет: Эта функция выполняет сложение.
print(add.author)      # Выведет: AI Assistant
print(add.__doc__)      # Выведет: Складывает два числа.
print(add.__name__)     # Выведет: add (благодаря functools.wraps)

Разъяснения:

  • add_metadata(metadata): Это наш декоратор, который принимает словарь metadata с парами ключ-значение, которые мы хотим добавить к функции.
  • decorator(func): Эта вложенная функция принимает декорируемую функцию func.
  • @functools.wraps(func): Ключевой момент! Эта строка импортирует и использует functools.wraps, который гарантирует, что обертка wrapper сохраняет важную информацию об исходной функции func, такую как её __name__ (имя), __doc__ (строка документации), и т.д. Без этого декоратора, add.__name__ выведет "wrapper" вместо "add", что затруднит отладку и понимание кода.
  • wrapper(*args, **kwargs): Это функция-обертка, которая вызывает исходную функцию func с переданными аргументами. Мы добавляем атрибуты *перед* возвратом этой обертки. Если нужно как-то изменять поведение функции, это место, где это нужно делать. В данном примере, мы просто вызываем исходную функцию без изменений.
  • setattr(wrapper, key, value): Мы перебираем пары ключ-значение в словаре metadata и используем setattr для добавления этих значений как атрибутов к обертке wrapper.
  • return wrapper: Декоратор возвращает модифицированную функцию (в данном случае, обертку с добавленными атрибутами).
  • @add_metadata(...): Синтаксис декоратора, который применяет декоратор add_metadata к функции add.

Почему это важно?

  • Чистота кода: Метаданные отделены от логики функции, что делает код более читаемым и поддерживаемым.
  • Многократное использование: Один и тот же декоратор можно применять к разным функциям.
  • Гибкость: Вы можете легко изменять и добавлять метаданные, не затрагивая код самой функции.

Альтернативные подходы (менее предпочтительные):

  • Можно напрямую добавлять атрибуты к функции без декоратора, но это менее элегантно и может загромождать код.
  • Использование func.__dict__.update(metadata) также работает, но setattr является более стандартным и читаемым способом.

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

0