装饰器,顾名思义,就是起装饰作用的东西。手机壳是一种装饰品。手机膜也是一种装饰品。
在编程的世界里,装饰器的作用,就是用一个壳子把一个函数包起来。以后每次调用的时候,实际上调用的是这个壳子。这个壳子先做一些操作,然后壳子再调用真正的函数。
装饰器可以是一个函数,也可以是一个类。今天就聊一下类装饰器。
类装饰器
昨天我们举了一个类装饰器的例子。我把它稍微改造了一下:
有一个函数cook(),也就是做饭。不是苹果CEO那个库克,是做饭的厨师。
我们希望每次做饭之前,都要先洗手。
这里的WashHandDecorator就是一个类装饰器。
我们通过@WashHandDecorator声明了用这个装饰器来装饰cook()函数
打印结果:
洗手装饰器组装中...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...
原理解释
上面的原理已经解释了一半了,还不够透彻。所以继续:
WashHandDecorator是装饰器。
它装饰了函数cook()以后,以后每次调用cook(),实际上是调用的WashHandDecorator类的实例,也就是调用了__call__函数,这个函数会再调用cook()。也就是先洗手,洗完手再去做饭。
其实装饰器的语法,也就是那个@WashHandDecorator,只是一个语法糖衣。不用这个语法,我们也可以实现同样的效果:
这个的执行结果和上面是完全一样的。
来看最关键的这一行cook=WashHandDecorator(cook):
参数中的cook代表的是上面定义的cook()函数,它作为参数传给了WashHandDecorator的构造函数。
而前面的变量名cook是新构造出来的WashHandDecorator的实例,只是故意让它的名字和函数名相同。
这样后面再调用cook()的时候,实际上不是调用的cook()函数,而是调用的这个对象实例。
对象一般不可以直接这样cook()调用的,除非它有__call__函数,而WashHandDecorator正好有这个函数。
__call__函数里实现了:先洗手,然后再调用原本的cook()函数。
这就是整个装饰的过程。使用装饰器的语法不过是自动帮我们实现了上面的过程。
所以一个类要成为装饰的,最重要的就是必须有__call__函数,这就和上一个三分钟呼应上了。