以下是一个无用的示例,它表明我无法用add
装饰我的my_decorator
函数,并提供了一些额外的参数:
def my_decorator(fn=None, message=None, **options):
assert fn is not None, 'fn is not found'
print('function is found')
print(message)
return fn
# This syntax works (but does not make sense...)
@my_decorator
def multiply(a, b):
return a * b
# This syntax does not work
@my_decorator(message='surprise')
def add(a, b):
return a + b
# This no sugar-syntax works
add = my_decorator(add, message='surprise') # Works
在第二个示例中,fn
被视为None
,并提高了AssertionError
,但使用的是“无糖”语法!
我的代码有什么问题? (Python 2.7。是的,我们将对其进行迁移...)
答案 0 :(得分:1)
my_decorator(message='surprise')
返回None
,因为它隐式使用了fn=None
。
然后,您尝试将None
应用于add
,如您所知,这是行不通的。
您需要一个参数化的函数来返回装饰器。非正式地,这称为参数化装饰器。
这是一个例子:
def make_decorator(message):
def decorator(func):
def new_func(*args, **kwargs):
print(message)
return func(*args, **kwargs)
return new_func
return decorator
@make_decorator(message='surprise')
def add(a, b):
return a + b
x = add(1, 2) # prints 'surprise' and sets x to 3
请注意
@make_decorator(message='surprise')
def add(a, b):
return a + b
等同于
def add(a, b):
return a + b
decorator = make_decorator(message='surprise')
add = decorator(add)
答案 1 :(得分:1)
首先,创建装饰器的最低要求是实现一个在参数中带有函数的函数(装饰器)。
例如,您可以编写:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called")
return f(*args, **kwargs)
return wrapper
# You can use your decorator w/o parameter:
@my_decorator
def multiply(a, b):
return a * b
您得到:
multiply(5, 8)
f() is called
40
如您所见,它可以工作,但装饰器不带任何参数。 因此,您需要创建一个装饰工厂:
def my_decorator_factory(message):
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + message)
return f(*args, **kwargs)
return wrapper
return my_decorator
# You can use your decorator factory with a parameter:
@my_decorator_factory(message="hi")
def multiply(a, b):
return a * b
您得到:
multiply(5, 8)
f() is called, saying: hi
40
但是,您希望能够像装饰器(不带参数)那样调用装饰器工厂。
因此,您需要更改装饰器工厂的签名以允许以下调用:
my_decorator_factory(message="something") # or
my_decorator_factory(function)
在这里,您需要检查第一个参数是否是其他函数:
import inspect
def my_decorator_factory(message=None):
if inspect.isfunction(message):
return my_decorator_factory(message=None)(message)
else:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + (message or "(empty)"))
return f(*args, **kwargs)
return wrapper
return my_decorator
@my_decorator_factory
def multiply(a, b):
return a * b
@my_decorator_factory(message='surprise')
def add(a, b):
return a + b
您得到:
multiply(5, 8)
f() is called, saying: (empty)
40
add(3, 2)
f() is called, saying: surprise
5
您可以考虑使用Wrapt来实现“好的”装饰器/装饰器工厂。