2025, Nov 27 06:01

Где применять functools.wraps в фабриках декораторов Python

Разбираем правильное место для functools.wraps в фабриках декораторов Python: почему это важно, как исправить код и избежать ошибок. Пошаговый пример.

Фабрики декораторов — удобный способ задавать параметры поведения, но они часто поднимают один и тот же вопрос: где именно следует применять functools.wraps? Если вы видели лишь базовые примеры декораторов в официальной документации, появление фабрики может вызвать сомнения.

Постановка задачи

Рассмотрим фабрику декораторов, которая перемножает список чисел, выводит результат, а затем вызывает обёрнутую функцию. Структура верная, но если вы только знакомитесь с фабриками, место для @wraps может быть неочевидным.

from functools import wraps

def product_chain(values):
    def apply_to(func_obj):
        def inner_exec(*args, **kwargs):
            acc = values[0]
            for nxt in values[1:]:
                acc = acc * nxt
            print(acc)
            return func_obj(*args)
        return inner_exec
    return apply_to

@product_chain(values=[1, 2, 3])
def announce(**kwargs):
    print("That was the multiplied number.")

announce()

Что здесь происходит

@wraps сам по себе является декоратором. Его импортируют из functools и ставят непосредственно над внутренней функцией-обёрткой. Хотя в документации это чаще показывают на примере обычного декоратора, при фабрике действует тот же принцип: вы всё равно декорируете именно обёртку, которую возвращаете.

Исправление

Импортируйте wraps из functools и примените его к функции-обёртке внутри фабрики, передав ей исходную функцию.

from functools import wraps

def product_chain(values):
    def apply_to(func_obj):
        @wraps(func_obj)
        def inner_exec(*args, **kwargs):
            acc = values[0]
            for nxt in values[1:]:
                acc = acc * nxt
            print(acc)
            return func_obj(*args)
        return inner_exec
    return apply_to

@product_chain(values=[1, 2, 3])
def announce(**kwargs):
    print("That was the multiplied number.")

announce()

Почему расположение важно

Применение @wraps к функции-обёртке в фабрике декораторов совпадает с практикой для обычных декораторов и снимает вопрос «какую именно функцию декорировать». Такой подход соответствует официальным рекомендациям и ведёт себя одинаково в обоих случаях.

Вывод

Работая с фабрикой декораторов, импортируйте wraps из functools и применяйте его к внутренней обёртке, которую возвращаете. Размещайте @wraps(original_function) прямо над определением обёртки. Этот шаблон такой же, как и в обычных декораторах, и делает использование ясным и предсказуемым.