Объединение декораторов в 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 без проблем. Дайте мне знать, если это сработает, я не тестировал его, но теоретически он должен.
какая там магия: во-первых, нам нужно получить параметры для наших декораторов. Таким образом, мы можем передайте их во вложенную функцию. Затем, мы принимаем нашу функцию в качестве аргумента, и последнее, мы получаем параметры для нашей функции. Мы можем вложить в гнездо столько, сколько нам нужно.