使用可选参数生成装饰器

2024-04-29 14:16:06 发布

您现在位置:Python中文网/ 问答频道 /正文

from functools import wraps

def foo_register(method_name=None):
    """Does stuff."""
    def decorator(method):
        if method_name is None:
            method.gw_method = method.__name__
        else:
            method.gw_method = method_name
        @wraps(method)
        def wrapper(*args, **kwargs):
            method(*args, **kwargs)
        return wrapper
    return decorator

示例:下面用foo_register来装饰my_function,而不是将其改为decorator

@foo_register
def my_function():
    print('hi...')

示例:以下操作如预期所示。

@foo_register('say_hi')
def my_function():
    print('hi...')

如果我想让它在两个应用程序中都能正常工作(一个使用method.__name__,另一个传入名称),我必须检查foo_register的内部,看看第一个参数是否是decorator,如果是,我必须:return decorator(method_name)(而不是return decorator)。这种“检查一下是不是可以打电话”的说法似乎很老套。有没有更好的方法来创建这样一个多用途的decorator?

我已经知道我可以要求调用decorator,但这不是一个“解决方案”。我希望API感觉自然。我妻子喜欢装饰,我不想破坏它。


Tags: namenoneregisterreturnfoomydefargs
3条回答

格伦-那时候我必须这么做。我想我很高兴没有“魔法”的方法来做这件事。我讨厌那些。

所以,这里是我自己的答案(方法名与上面不同,但概念相同):

from functools import wraps

def register_gw_method(method_or_name):
    """Cool!"""
    def decorator(method):
        if callable(method_or_name):
            method.gw_method = method.__name__
        else:
            method.gw_method = method_or_name
        @wraps(method)
        def wrapper(*args, **kwargs):
            method(*args, **kwargs)
        return wrapper
    if callable(method_or_name):
        return decorator(method_or_name)
    return decorator

示例用法(两个版本的工作原理相同):

@register_gw_method
def my_function():
    print('hi...')

@register_gw_method('say_hi')
def my_function():
    print('hi...')

通过这里和其他地方的答案的帮助和一系列的尝试和错误,我发现实际上有一种更简单和通用的方法可以让decorator接受可选参数。它确实检查了用它调用的args,但没有其他方法可以这样做。

关键是要装饰你的装饰师。

泛型decorator decorator代码

这里是decorator decorator(此代码是通用的,任何需要可选arg decorator的人都可以使用):

def optional_arg_decorator(fn):
    def wrapped_decorator(*args):
        if len(args) == 1 and callable(args[0]):
            return fn(args[0])

        else:
            def real_decorator(decoratee):
                return fn(decoratee, *args)

            return real_decorator

    return wrapped_decorator

用法

使用它就像:

  1. 像平常一样创建一个装饰器。
  2. 在第一个目标函数参数之后,添加可选参数。
  3. optional_arg_decorator装饰装饰器

示例:

@optional_arg_decorator
def example_decorator_with_args(fn, optional_arg = 'Default Value'):
    ...
    return fn

测试用例

对于您的用例:

因此,对于您的情况,使用传入的方法名或__name__(如果None)保存函数的属性:

@optional_arg_decorator
def register_method(fn, method_name = None):
    fn.gw_method = method_name or fn.__name__
    return fn

添加修饰方法

现在您有了一个带或不带参数的装饰器:

@register_method('Custom Name')
def custom_name():
    pass

@register_method
def default_name():
    pass

assert custom_name.gw_method == 'Custom Name'
assert default_name.gw_method == 'default_name'

print 'Test passes :)'

我所知道的最干净的方法是:

import functools


def decorator(original_function=None, optional_argument1=None, optional_argument2=None, ...):

    def _decorate(function):

        @functools.wraps(function)
        def wrapped_function(*args, **kwargs):
            ...

        return wrapped_function

    if original_function:
        return _decorate(original_function)

    return _decorate

说明

当调用decorator时没有如下可选参数:

@decorator
def function ...

函数作为第一个参数传递,decorate将按预期返回decorated函数。

如果使用一个或多个可选参数调用decorator,如下所示:

@decorator(optional_argument1='some value')
def function ....

然后使用值为None的函数参数调用decorator,这样会返回一个decorate函数,如预期的那样。

Python 3

注意,上面的decorator签名可以使用Python 3特定的*,语法进行改进,以强制安全地使用关键字参数。只需将最外层函数的签名替换为:

def decorator(original_function=None, *, optional_argument1=None, optional_argument2=None, ...):

相关问题 更多 >