python的装饰器其实是一个语法糖,第一行是@跟着一个表达式;第二行必须以def或者class起始(亦即函数或者class的定义)。
python的装饰器,必须是可调用的对象,而且必须是可以以一个参数进行调用。注意,对返回参数的个数是不做限制的!
那么,python中有几种可调用对象呢?大致有四种:
1)函数对象,这个不用多说什么了吧;
2)类对象,我们生成类实例的时候,都是用类对象调用生成的;
3)类的三种方法:实例方法、类方法、静态方法;
4)如果类实现了`__call__`方法,则类的实例对象也是可调用的;
装饰器本质上就是一个语法糖,它等价于先定义了函数/类之后,将函数对象/类对象作为参数调用装饰器,然后将返回值赋给原本代表函数对象/类对象的变量。
事实上,只要装饰器能够成功执行,不管它如何处理的,python都不会报错。即使返回值不是同类的函数或者类对象,而是其它的什么东西,比如基本类型,乃至列表/元组都无所谓。
好了,我们现在差不多,就知道装饰器这玩意是一个多么自由的东西了。其实python里面n多东西都这么自由。
根据装饰器的输入和输出,我们可以将装饰器分成如下几类
1)吃进一个函数,吐出一个修饰过的函数。
这是我们自己最常写的一种装饰器了,通常我们通过装饰器,来进行log、缓存、类型检验、错误捕捉等等。
2)吃进一个函数,返回一个class对象。
这种情况比较罕见,至少我没有见过。
3)吃进一个函数,吐出一个class的实例。
最典型的,比如property,它可以吃进一个函数,返回一个描述器的实例(注意返回的实例的身份肯定是类的属性)。staticmethod和classmethod也是一样的道理。
还有一种情况,也就是返回一个实现了`__call__`方法的class对象的实例。这种相当于一个可更新环境的闭包,有时候挺有用。
4)吃进一个函数,吐出一个不可调用对象。
基本上,这是起到一个提前计算的作用。比如说一个装饰器可以接受一个函数,然后预先计算出函数的返回值,然后直接将返回值赋给原本代表函数对象/类对象的变量。
5)吃进一个class对象,吐出一个函数。
比如返回一个可以使用参数来返回该class对象的实例的函数,但同时也进行log、缓存、类型检验、错误捕捉等等。
6)吃进一个class对象,吐出一个class对象。
和第一种类似,只不过是对class对象进行完善,比如functools.total_ordering,它就是可以完善class对象的比较运算的魔术方法。