全部版块 我的主页
论坛 数据科学与人工智能 数据分析与数据科学 数据分析师(CDA)专版
352 0
2022-04-24

作者:麦叔

来源:麦叔编程

装饰器的本质

先简单说一下什么是装饰器。

装饰器,顾名思义,就是起装饰作用的东西。手机壳是一种装饰品。手机膜也是一种装饰品。

在编程的世界里,装饰器的作用,就是用一个壳子把一个函数包起来。以后每次调用的时候,实际上调用的是这个壳子。这个壳子先做一些操作,然后壳子再调用真正的函数。

装饰器可以是一个函数,也可以是一个类。今天就聊一下类装饰器。

类装饰器

昨天我们举了一个类装饰器的例子。我把它稍微改造了一下:

  • 有一个函数cook(),也就是做饭。不是苹果CEO那个库克,是做饭的厨师。
  • 我们希望每次做饭之前,都要先洗手。
  • 这里的WashHandDecorator就是一个类装饰器。
  • 我们通过@WashHandDecorator声明了用这个装饰器来装饰cook()函数

先看一下代码和执行结果,思考一下:

class WashHandDecorator(object): def __init__(self, f): self.f = f      print("洗手装饰器组装中...") def __call__(self): print("先洗手,再做饭...")      self.f() @WashHandDecorator def cook(): print("做饭中...美味香喷喷...但是自己不能吃...")cook()cook()cook()

打印结果:

洗手装饰器组装中...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...先洗手,再做饭...做饭中...美味香喷喷...但是自己不能吃...原理解释

上面的原理已经解释了一半了,还不够透彻。所以继续:

  • WashHandDecorator是装饰器。
  • 它装饰了函数cook()以后,以后每次调用cook(),实际上是调用的WashHandDecorator类的实例,也就是调用了 __call__函数,这个函数会再调用cook()。也就是先洗手,洗完手再去做饭。

其实装饰器的语法,也就是那个@WashHandDecorator,只是一个语法糖衣。不用这个语法,我们也可以实现同样的效果:

class WashHandDecorator(object): def __init__(self, f): self.f = f      print("洗手装饰器组装中...") def __call__(self): print("先洗手,再做饭...")      self.f() def cook(): print("做饭中...美味香喷喷...但是自己不能吃...") ## 最关键的一行,魔法就在这里!!!!!! cook = WashHandDecorator(cook) cook()cook()cook()

这个的执行结果和上面是完全一样的。

来看最关键的这一行cook = WashHandDecorator(cook):

  • 参数中的cook代表的是上面定义的cook()函数,它作为参数传给了WashHandDecorator的构造函数。
  • 而前面的变量名cook是新构造出来的WashHandDecorator的实例,只是故意让它的名字和函数名相同。
  • 这样后面再调用cook()的时候,实际上不是调用的cook()函数,而是调用的这个对象实例。
  • 对象一般不可以直接这样cook()调用的,除非它有__call__函数,而WashHandDecorator正好有这个函数。
  • __call__函数里实现了:先洗手,然后再调用原本的cook()函数。
  • 这就是整个装饰的过程。使用装饰器的语法不过是自动帮我们实现了上面的过程。
  • 所以一个类要成为装饰的,最重要的就是必须有__call__函数,这就和上一个三分钟呼应上了。

懂了吗?没懂再看一遍。如果真懂了,有没有一种恍然大悟,甚至醍醐灌顶的感觉?



      相关帖子DA内容精选
  • 大厂数据分析面试指南!来自亚马逊、谷歌、微软、头条、美团的面试问题!
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群