我知道这个问题很旧,但是有些评论是新的,尽管所有可行的解决方案本质上都是相同的,但大多数解决方案都不是很干净也不易于阅读。
就像thobe的回答所说,处理这两种情况的唯一方法是检查这两种情况。最简单的方法是简单地检查是否有单个参数并且它是callabe(注意:如果您的装饰器仅接受1个参数并且恰好是一个可调用对象,则需要额外检查):
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)
在第一种情况下,您可以执行任何普通装饰器所执行的操作,返回传入函数的修改或包装版本。
在第二种情况下,您返回一个“新”修饰符,该修饰符以某种方式使用通过 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
现在,我们可以使用@doublewrap装饰我们的装饰器,它们将在有或无参数的情况下工作,但有一个警告:
我在上面提到过,但是应该在这里重复一遍,此装饰器中的检查对装饰器可以接收的参数进行了假设(即,它无法接收单个可调用的参数)。由于我们现在使它适用于任何生成器,因此需要牢记或修改它,以防矛盾。
下面演示其用法:
def test_doublewrap(): from util import doublewrap from functools import wraps @doublewrap def mult(f, factor=2): '''multiply a function's return value''' @wraps(f) def wrap(*args, **kwargs): return factor*f(*args,**kwargs) return wrap # try normal @mult def f(x, y): return x + y # try args @mult(3) def f2(x, y): return x*y # try kwargs @mult(factor=5) def f3(x, y): return x - y assert f(2,3) == 10 assert f2(2,5) == 30 assert f3(8,1) == 5*7



