将Python装饰器与参数合并为一个

Lin*_*Lin 15 python python-decorators

我正在使用来自两个不同库的两个不同的装饰器.让我们说:@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):
    ...
Run Code Online (Sandbox Code Playgroud)

由于它每次都会发生,我想创建一个处理它们的自定义装饰器.就像是:

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

我如何实现这一目标?

Glo*_*eye 13

我认为你不应该 - 使用装饰器的原始名称提供更好的可读性.

但是,如果你真的想,你可以这样做:

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):
    ...
Run Code Online (Sandbox Code Playgroud)

根据评论,这里是另一种方法:

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
Run Code Online (Sandbox Code Playgroud)

对某些人来说,这可能看起来更简单.然而,有Python经验的人习惯于使用@来应用装饰器 - 即使仅仅因为这个原因,我更喜欢我的第一个选项.我知道我第一次阅读这段代码需要花费三倍的时间来理解它的作用.

它真的很简单 - 只需编写一个装饰器,它返回另一个装饰器,它将使用其他两个装饰器装饰它的内部函数;)

为了良好的习惯,使用functools.wraps也许是个好主意.它是标准库,可以帮助调试和使用交互式控制台:https://docs.python.org/3.7/library/functools.html

一般来说,我会说一个额外的代码行更值得分别使用装饰器的清晰度.当你在3个月内阅读自己的代码时,你会感谢你自己.