Объединение декораторов в Python с аргументами в один [дубликат]

этот вопрос уже есть ответ здесь:

Я использую двух разных декораторов из двух разных библиотек. Скажем:@decorator1(param1, param2) и @decorator2(param3, param4). Что я часто использую во многих функциях как:

from moduleA import decorator1
from moduleB import decorator2

@decorator2(foo='param3', bar='param4')
@decorator1(name='param1', state='param2')
def myfunc(funcpar1, funcpar2):
    ...

так как это происходит каждый время, я хотел бы создать пользовательский декоратор это касается их обоих. Что-то вроде:

@mycustomdecorator(name='param1', state='param2',
                   foo='param3', bar='param4')
def myfunc(funcpar1, funcpar2):
    ...

как мне этого достичь?

3 ответов


Я бы сказал, что вы не должны - использование оригинальных имен для декораторов дает гораздо лучшую читаемость.

однако, если вы действительно хотите, вы можете сделать это так:

import functools

from moduleA import decorator1
from moduleB import decorator2

def my_decorator(foo, bar, name, state):
    def inner(func):
        @decorator2(foo=foo, bar=bar)
        @decorator1(name=name, state=state)
        @functools.wraps(func)  # Not required, but generally considered good practice
        def newfunc(*args, **kwargs)
            return func(*args, **kwargs)
        return newfunc
    return inner

@my_decorator(foo='param3', bar='param4', name='param1', state='param2')
def myfunc(funcpar1, funcpar2):
    ...

основываясь на комментариях, вот альтернативный метод:

def my_decorator(foo, bar, name, state):
    def inner(func):
        # Please note that for the exact same result as in the other one, 
        # the order of decorators has to be reversed compared to normal decorating
        newfunc = decorator1(name=name, state=state)(func)
        newfunc = decorator2(foo=foo, bar=bar)(newfunc)
        # Note that functools.wraps shouldn't be required anymore, as the other decorators should do that themselves
        return newfunc
    return inner

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

это просто на самом деле-просто напишите декоратор, который возвращает другой декоратор, который будет иметь его внутреннюю функцию, украшенную двумя другими декораторами;)

также может быть хорошей идеей использовать functools.обертывания, ради хороших привычек. Это стандартная библиотека и очень помогает при отладке и использовании интерактивной консоли: https://docs.python.org/3.7/library/functools.html

В общем, я бы сказал, что одна дополнительная строка кода более чем стоит ясности использования декораторов отдельно. Вы поблагодарите себя, когда прочитаете свой собственный код через 3 месяца.


Это просто простая композиция функций, где decorator1 и decorator2 возвращают функции, которые вы хотите создать. Реальная работа может быть абстрагирована в функцию compose.

# In the special case of composing decorators, the lambda expression
# only needs to be defined with a single argument, like
#
#   lambda func: f(g(func))
#
# but I show a more general form.
def compose(f, g):     
    return lambda *args, **kwargs: f(g(*args, **kwargs))

def customdecorator(foo, bar, name, state):
    return compose(decorator2(foo=foo, bar=bar),
                   decorator1(name=name, state=state))

декоратор-это не что иное, как синтаксический сахар для синтаксиса, такого как func = decorator(func).

таким образом, вы можете легко сделать свой собственный декоратор, который делает все, что вы хотите с следующий синтаксис:

def mycustomdecorator(foo, bar, name, state)
    def innerdecorator(func):
        func = decorator1(foo=foo, bar=bar)(func)
        func = decorator2(name=name, state=state)(func)
        return func
    return innerdecorator

после этого вы сможете использовать @mycustomdecorator без проблем. Дайте мне знать, если это сработает, я не тестировал его, но теоретически он должен.

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