摘要:本篇主要描述什么是python的装饰器,以及装饰器是如何使用和工作原理。
装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
我们来看个例子:
1 | def demo1(): |
但现在我需要在调用函数时记录执行日志,于是在代码中添加:
1 | def demo1(): |
但是如果其他函数也需要的话一个一个添加就会很麻烦。
接下来我们可以写一个函数来代替一个个添加。
1 | def use_logging(func): |
逻辑上不难理解, 但是这样的话,我们每次都要将一个函数作为参数传递给use_logging函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行demo1(),但是现在不得不改成use_logging(demo1)。那么有没有更好的方式的呢?当然有,答案就是装饰器。
简单装饰器
先看这个新的例子来解释闭包:
1 | # print_msg是外围函数 |
msg
是一个局部变量,在print_msg
函数执行之后应该就不会存在了。但是嵌套函数引用了这个变量,将这个局部变量封闭在了嵌套函数中,这样就形成了一个闭包。
结合这个例子再看维基百科的解释,就清晰明了多了。闭包就是引用了自有变量的函数,这个函数保存了执行的上下文,可以脱离原本的作用域独立存在。
下面看看python中的装饰器:
1 | import functools |
调用它:
1 |
|
输出:
装饰器在调用是时候使用了@语法,其实实际上的调用如下:
1 | def test(p): |
@
语法只是将函数传入装饰器函数,并无神奇之处。
值得注意的是@functools.wraps(func)
,这是python提供的装饰器。它能把原函数的元信息拷贝到装饰器里面的 func 函数中。函数的元信息包括docstring、name、参数列表等等。可以尝试去除@functools.wraps(func)
,你会发现test.__name__
的输出变成了wrapper。
带参数的装饰器
装饰器允许传入参数,一个携带了参数的装饰器将有三层函数,如下所示:
1 | import functools |
看到这个代码是不是又有些疑问,内层的decorator函数的参数func是怎么传进去的?和上面一般的装饰器不大一样啊。
其实道理是一样的,将其@
语法去除,恢复函数调用的形式一看就明白了:
1 | # 传入装饰器的参数,并接收返回的decorator函数 |
输出结果与正常使用装饰器相同:
至此,装饰器这个有点费解的特性也没什么神秘了。
装饰器这一语法体现了Python中函数是第一公民,函数是对象、是变量,可以作为参数、可以是返回值,非常的灵活与强大。
Python中引入了很多函数式编程的特性,需要好好学习与体会。