我正在使用来自两个不同库的两个不同的装饰器。可以说:@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):
...
我该如何实现?
答案 0 :(得分:12)
我认为您不应该-使用装饰器的原始名称可提供更好的可读性。
但是,如果您确实愿意,可以这样做:
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.wraps也可能是一个好主意。它是标准库,有助于调试和交互式控制台的使用:https://docs.python.org/3.7/library/functools.html
尽管如此,总的来说,我要说的是,额外多一行代码比单独使用装饰器更为清晰。再过三个月阅读自己的代码,您将感激不尽。
答案 1 :(得分:4)
这只是简单的函数组合,其中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))
答案 2 :(得分:1)
Decorator只不过是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。让我知道这是否有效,但我没有进行测试,但从理论上讲应该可以。
那里的魔力是什么:首先,我们需要获取装饰器的参数。这样,我们就可以将它们传递给嵌套函数。然后,我们接受函数作为参数,最后,我们获取函数的参数。我们可以根据需要嵌套def-s。