我们继续讲解Python装饰器。上一讲我们把装饰器的基本原理讲了,这一讲我们来谈谈装饰器相关的语法。
1. @是语法糖
下面这两段代码是等效的,与其让decorator显式接收一个func作为参数,然后将调用后得到的结果赋值给func变量,我们可以直接在定义的func前加上@decorator
语句也能达到相同的效果,而且更方便。我们把这个@语句称为语法糖。
所谓语法糖可以理解成是实现特定功能的捷径,它简化了我们编写代码的方式。这里的@语句在定义函数时使用,使代码更容易阅读并且可以立即意识到应用了装饰器。值得一提的是,在Python中语法糖是大量存在的,而不仅仅是局限于装饰器中。
简单一点的,假如我们要构建一个列表,此时的[1,2,3]
相对于list(1,2,3)
就是语法糖。复杂一点的,假如我们要创建一个迭代器,写一个生成器函数相对于直接构造一个迭代器的类就是一种语法糖,而写一个生成器表达式则是相比于生成器函数更进一步的语法糖。
2. 装饰器在被装饰的函数定义后立即运行
理解装饰器在何时运行的问题是我们理解被装饰过的函数的种种行为的基础。
可以看到,装饰器函数在自身和被装饰函数定义完成后,会立即被调用,也就是说装饰器在被加载到模块后会立即运行。而装饰后的函数f,只有显式调用才会被执行。那么原函数呢?这里原函数和被装饰后的函数是同一个,更常见的情况是被装饰后的函数包含对原函数的引用,显式调用被装饰后的函数也会顺带调用原函数。
3. 对一个函数应用多个装饰器
对一个函数应用多个装饰器从整体上来理解就是对装饰后得到的被装饰函数再进行装饰。
多个装饰器也不限于两个,也可以多次使用同一个装饰器
下面把整段代码贴出来和大家一起分析下
这段代码中有以下几点值得注意:
- 这里面的
f
、warpper1
、warpper2
都是使用相同的位置参数,这一点很重要,是确保装饰器能正确运行的前提。 - 这个装饰的过程是自里向外的,我们先装饰
f
得到wrapper1
,再装饰wrapper1
得到warpper2
。
注意到以上两点后我们再来看装饰后的f
的运行情况。当我们调用f("haha")
时,其实相当于运行warpper2("haha")
,warpper2
在运行时又会调用warpper1
,并把从位置参数text接收来的变量值"haha"传给wrapper1
,wrapper1
在运行时又会调用真正的f
,然后也把从位置参数text接收来的变量值"haha"传给f
,最后f
运行完成,又返回到wrapper1
,wapper1
运行完成返回到wrapper2
,wrapper2
运行完成这个函数才算结束。
4. *args, **kwargs
通常我们在定义装饰器时,为了装饰器能更加的通用,需要考虑到被装饰的函数参数个数不一致的问题。就比如上面定义的的那个装饰器,它要求被装饰的函数只能有一个参数,因为我们后续调用时,只能给被装饰后的函数wrapper
一个参数。而wrapper
恰恰是代替原函数f
接受调用的。
知道了问题所在,我们让wrapper接受可变的参数个数也就解决问题了。