oro*_*aki 56 python decorator wrapper
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
Run Code Online (Sandbox Code Playgroud)
例如:下面的装饰my_function用foo_register的,而不是它曾经做对decorator.
@foo_register
def my_function():
print('hi...')
Run Code Online (Sandbox Code Playgroud)
示例:以下按预期工作.
@foo_register('say_hi')
def my_function():
print('hi...')
Run Code Online (Sandbox Code Playgroud)
如果我希望它在两个应用程序中正常工作(一个使用method.__name__和一个传递名称),我必须检查内部foo_register是否第一个参数是装饰器,如果是,我必须:( return decorator(method_name)而不是return decorator).这种"检查它是否可以调用"似乎非常hackish.有没有更好的方法来创建这样的多用途装饰器?
PS我已经知道我可以要求装饰器被调用,但这不是一个"解决方案".我希望API感觉自然.我的妻子喜欢装饰,我不想破坏它.
ben*_*nte 51
我知道这样做最干净的方法如下:
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
Run Code Online (Sandbox Code Playgroud)
说明
调用装饰器时没有像这样的可选参数:
@decorator
def function ...
Run Code Online (Sandbox Code Playgroud)
函数作为第一个参数传递,decorate按预期返回修饰函数.
如果使用一个或多个可选参数调用装饰器,如下所示:
@decorator(optional_argument1='some value')
def function ....
Run Code Online (Sandbox Code Playgroud)
然后使用值为None的函数参数调用decorator,因此返回一个装饰函数,如预期的那样.
Python 3
请注意,上面的装饰器签名可以使用Python 3特定的*,语法进行改进,以强制安全使用关键字参数.只需将最外层函数的签名替换为:
def decorator(original_function=None, *, optional_argument1=None, optional_argument2=None, ...):
Run Code Online (Sandbox Code Playgroud)
Nic*_*ole 38
通过这里和其他地方的答案以及一堆反复试验的帮助,我发现实际上有一种更容易和通用的方法使装饰器采用可选参数.它确实检查了它被调用的args,但没有任何其他方法可以做到这一点.
关键是装饰你的装饰.
这是装饰器装饰器(此代码是通用的,可供需要可选arg装饰器的任何人使用):
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
Run Code Online (Sandbox Code Playgroud)
使用它就像:
optional_arg_decorator例:
@optional_arg_decorator
def example_decorator_with_args(fn, optional_arg = 'Default Value'):
...
return fn
Run Code Online (Sandbox Code Playgroud)
因此,对于你的情况,以节省属性与传入的方法名称的功能或__name__如果无:
@optional_arg_decorator
def register_method(fn, method_name = None):
fn.gw_method = method_name or fn.__name__
return fn
Run Code Online (Sandbox Code Playgroud)
现在你有一个可以使用或不使用args的装饰器:
@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 :)'
Run Code Online (Sandbox Code Playgroud)
oro*_*aki 28
格伦 - 我必须这样做.我想我很高兴没有"神奇"的方法来做到这一点.我讨厌那些.
所以,这是我自己的答案(方法名称与上述不同,但概念相同):
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
Run Code Online (Sandbox Code Playgroud)
示例用法(两个版本的工作方式相同):
@register_gw_method
def my_function():
print('hi...')
@register_gw_method('say_hi')
def my_function():
print('hi...')
Run Code Online (Sandbox Code Playgroud)
小智 11
怎么样
from functools import wraps, partial
def foo_register(method=None, string=None):
if not callable(method):
return partial(foo_register, string=method)
method.gw_method = string or method.__name__
@wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
Run Code Online (Sandbox Code Playgroud)
以下是我对@ NickC答案的改编,其中包括以下增强功能:
import functools
def optional_arg_decorator(fn):
@functools.wraps(fn)
def wrapped_decorator(*args, **kwargs):
is_bound_method = hasattr(args[0], fn.__name__) if args else False
if is_bound_method:
klass = args[0]
args = args[1:]
# If no arguments were passed...
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
if is_bound_method:
return fn(klass, args[0])
else:
return fn(args[0])
else:
def real_decorator(decoratee):
if is_bound_method:
return fn(klass, decoratee, *args, **kwargs)
else:
return fn(decoratee, *args, **kwargs)
return real_decorator
return wrapped_decorator
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
18541 次 |
| 最近记录: |