这应该可以,除非您希望将函数用作
你的装潢师,在这种情况下,装潢师
will wrongly assume it has no arguments. It will also fail
if this decoration is applied to another decoration that
不返回函数类型。
另一种方法是要求
decorator function is always called, even if it is with no arguments.
在这种情况下,您的第二个示例如下所示:
I know this question is old, but some of the comments are new, and while all of the viable solutions are essentially the same, most of them aren't very clean or easy to read.
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# called as @decorator
else:
# called as @decorator(*args, **kwargs)
在第一种情况下,您可以执行任何普通装饰器都会执行的操作,返回传入函数的修改版本或包装版本。
In the second case, you return a 'new' decorator that somehow uses the information passed in with *args, **kwargs.
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# actual decorated function
return f(args[0])
else:
# decorator arguments
return lambda realf: f(realf, *args, **kwargs)
return new_dec
def meta_wrap(decor):
@functools.wraps(decor)
def new_decor(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# this is the double-decorated f.
# Its first argument should not be a callable
doubled_f = decor(args[0])
@functools.wraps(doubled_f)
def checked_doubled_f(*f_args, **f_kwargs):
if callable(f_args[0]):
raise ValueError('meta_wrap failure: '
'first positional argument cannot be callable.')
return doubled_f(*f_args, **f_kwargs)
return checked_doubled_f
else:
# decorator arguments
return lambda real_f: decor(real_f, *args, **kwargs)
return new_decor
下面是这个 meta_wrap故障安全版本的一些测试用例。
@meta_wrap
def baddecor(f, caller=lambda x: -1*x):
@functools.wraps(f)
def _f(*args, **kwargs):
return caller(f(args[0]))
return _f
@baddecor # used without arg: no problem
def f_call1(x):
return x + 1
assert f_call1(5) == -6
@baddecor(lambda x : 2*x) # bad case
def f_call2(x):
return x + 1
f_call2(5) # raises ValueError
# explicit keyword: no problem
@baddecor(caller=lambda x : 100*x)
def f_call3(x):
return x + 1
assert f_call3(5) == 600
from functools import partial, wraps
def decorator(func=None, foo='spam'):
if func is None:
return partial(decorator, foo=foo)
@wraps(func)
def wrapper(*args, **kwargs):
# do something with `func` and `foo`, if you're so inclined
pass
return wrapper
是的,你可以这样做
@decorator()
def f(*args, **kwargs):
pass
没有时髦的变通方法,我发现它奇怪的外观,我喜欢有选择,简单的装饰与 @decorator。
As for the secondary mission objective, redirecting a function's output is addressed in this Stack Overflow post.
from decopatch import function_decorator
@function_decorator
def add_tag(tag='hi!'):
"""
Example decorator to add a 'tag' attribute to a function.
:param tag: the 'tag' value to set on the decorated function (default 'hi!).
"""
def _apply_decorator(f):
"""
This is the method that will be called when `@add_tag` is used on a
function `f`. It should return a replacement for `f`.
"""
setattr(f, 'tag', tag)
return f
return _apply_decorator
而在“平面模式”中,方法直接是应用装饰符时将执行的代码。它被注入修饰函数对象 f:
from decopatch import function_decorator, DECORATED
@function_decorator
def add_tag(tag='hi!', f=DECORATED):
"""
Example decorator to add a 'tag' attribute to a function.
:param tag: the 'tag' value to set on the decorated function (default 'hi!).
"""
setattr(f, 'tag', tag)
return f