装饰器接受一个函数并返回一个新函数来替换原来的函数。它会立即进行评估。
所以:
def decorate(func):
print("Decorate!")
return func
@decorate
def add(x, y):
return x + y
将打印“装饰!”即使没有调用add。
您要做的是将原始函数“包装”在一个更新的函数中,该函数执行一些工作,然后调用原始函数。
def decorate(func):
def _wrap(*args, **kwargs):
print("Start")
return func(*args, **kwargs)
return _wrap
@decorate
def add(x, y):
return x + y
现在“add”将替换为_wrap,调用它时将首先运行您的打印语句,然后使用相同的参数调用原始函数并返回结果。如果您还想运行一些“发布”逻辑,您可以利用 try...finally:
def decorate(func):
def _wrap(*args, **kwargs):
print("Start")
try:
return func(*args, **kwargs)
finally:
print("End")
return _wrap
@decorate
def add(x, y):
return x + y
finally 块保证执行。如果您不关心在错误情况下打印“End”,您还可以存储返回结果:
def decorate(func):
def _wrap(*args, **kwargs):
print("Start")
result = func(*args, **kwargs)
print("End")
return result
return _wrap
@decorate
def add(x, y):
return x + y
如果你需要将参数传递给装饰器,你只需创建一个装饰器工厂,即一个接受一些参数并返回一个装饰函数的函数。
def makeDecorator(text):
def decorate(func):
def _wrap(*args, **kwargs):
print(f"Start: {text}")
try:
return func(*args, **kwargs)
finally:
print("End")
return _wrap
return decorate
@makeDecorator(text="Hi")
def add(x, y):
return x + y
关键是装饰器“替换”了被装饰的对象。所以如果你在装饰一个函数,你需要返回一个函数。装饰函数本身并不是调用add() 时调用的函数。
附加提示:当您返回 _wrap 函数时,其名称将是“_wrap”,而不是原始函数的名称。 functools 标准库提供了一些帮助器,例如 wraps 装饰器,您可以在 _wrap 上使用它来赋予它与原始函数相同的名称。对调试很有用。