装饰器在大工程中比较常见,那么如何理解装饰器呢?打个比方,假如你建好了一栋大房子,建好后还想加一些功能,这个时候房子的主体结构是不能动了,只好在现有房子的基础上做一些装饰/装修。这些装饰在不影响/不修改原来房子功能的基础上,增加了美观等功能。
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 装 饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个 函数或可调用对象。[2]
@decorate
def target():
print('running target()')
def target():
print('running target()')
target = decorate(target)
上述两种写法的代码效果一样。
上面出现的@
符号就是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作。
注意:Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例子如下:
def foo():
print("foo")
def bar(func):
func()
foo()
bar(foo)
bar
函数调用:
# 装饰器通常把函数替换成另一个函数
def deco(func):
def inner():
print('running inner()')
return inner # deco 返回 inner 函数对象
@deco
def target(): # 使用 deco 装饰 target。
print('running target()')
# target = deco(target) # 有了语法糖这句可以省略 相当于 target = wrapper
target() # 调用被装饰的 target 其实会运行 inner。
running target()
target # 审查对象,发现 target 现在是 inner 的引用。
.inner()>
# 装饰器通常把函数替换成另一个函数
def deco(func):
def inner():
print('running inner()')
return func()
return inner # deco 返回 inner 函数对象
@deco
def target(): # 使用 deco 装饰 target。
print('running target()')
# target = deco(target) # 有了语法糖这句可以省略 相当于 target = wrapper
target() # 调用被装饰的 target 其实会运行 inner。
running inner()
registry = [] # registry 保存被 @register 装饰的函数引用。
def register(func): # register 的参数是一个函数。
print('running register(%s)' % func) # 为了演示,显示被装饰的函数。
# func.__name__ 显示被装饰函数的名字
registry.append(func) # 把 func 存入 registry。
return func # 返回 func:必须返回函数;这里返回的函数与通过参数传入的一样。
@register
def f1():
print('running f1()')
@register
def f2():
print('running f2()')
def f3():
print('running f3()')
def main(): # main 显示 registry,然后调用 f1()、f2() 和 f3()。
print('running main()')
print('registry ->', registry)
f1()
f2()
f3()
if __name__=='__main__':
main()
running register()
running register()
running main()
registry -> [, ]
running f1()
running f2()
running f3()
注意,register 在模块中其他函数之前运行(两次)。调用register 时,传给它的参数是被装饰的函数,例如 。
上面例子主要想强调,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了 Python 程序员所说的导入时和运行时之间的区别。
考虑到装饰器在真实代码中的常用方式,上面例子有两个不寻常的地方。
[1] 廖雪峰 装饰器 [2] 流畅的python
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=29v0kx6sv4w0s