Python - правильный порядок применения декораторов - PullRequest
2 голосов
/ 06 октября 2010

Я украшаю функцию как таковую:

def some_abstract_decorator(func):
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

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

def some_abstract_decorator(func):
    @wraps(func)
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

(я, конечно, также применяю wraps внутри another_lower_level_decorator)

Ответы [ 3 ]

2 голосов
/ 06 октября 2010

Это верно.Это работает так:

  • wrapper.Он вызывает func со своими аргументами. Вызывается
  • another_lower_level_decorator с аргументом wrapper.Функция, которую она возвращает, становится новым значением wrapper.
  • wraps(func), вызывается для создания оболочки, которая будет применять имя / docstring / etc.func любой функции, к которой она вызывается.
  • Возвращаемое значение wraps(func), то есть созданная функция-обертка, передается текущему значению wrapper.Помните, это было возвращаемое значение из another_lower_level_decorator.
  • wraps(func)(wrapper) становится новым значением wrapper.
  • Это значение возвращается some_abstract_decorator, что делает эту функцию подходящейдля использования в качестве декоратора.

Или, в любом случае, это действительно так.Я думаю, что на практике wrapper назначается только один раз.

2 голосов
/ 06 октября 2010

Попробуйте:

from functools import wraps    

def another_lower_level_decorator(func):
    @wraps( func )
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapped

def some_abstract_decorator(func):
    @wraps(func)
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper


@some_abstract_decorator
def test():
    """ This is a docstring that should be on the decorated function """
    pass

help(test)

Отпечатки:

Help on function test in module __main__:

test(*args, **kwargs)
    This is a docstring that should be on the decorated function

Как видите, это работает!Строка документации есть и присвоено имя.

Но это работает точно так же:

def some_abstract_decorator(func):
    @another_lower_level_decorator
    @wraps(func)
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

wraps просто исправляет строки / имена документов.Пока все декораторы используют wraps, порядок их применения не имеет значения

Кстати, существует гораздо более классная библиотека декораторов :

from decorator import decorator

@decorator
def another_decorator(func, *args, **kwargs):
    return func(*args, **kwargs)

@decorator
@another_decorator
def some_abstract_decorator(func, *args, **kwargs):
    # ... details omitted
    return func(*args, **kwargs)


@some_abstract_decorator
def test(x):
    """ this is a docstring that should be on the decorated function """
    pass
1 голос
/ 06 октября 2010

Да, это выглядит правильно для меня. @another_lower_level_decorator вернет функцию, которую @wraps обернет так, чтобы она имела то же имя, что и func.

...