首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

史上最全的python装饰器教程,希望对你有帮助!

假如你是一家视频网站的后端开发工程师,你们网站有以下几个版块:

在视频刚上线初期,为了吸引客户,你们采取了免费政策,所有视频免费观看,迅速吸引了一大批用户,免费一段时间后,发现每天巨大的带宽费用公司承受不起,所以准备对比较受欢迎的几个版块进行收费,其中包括“欧美”和“北京”专区,拿到这个需求后,想了想,想收费就需要对用户进行认证,认证通过后,再判断这个用户是否是VIP付费会员就可以了,是VIP就让他观看,不是VIP就不让他观看呗!你觉得这个需求很简单,因为要对多个版块进行认证,那应该把认证功能提取出来单独写个模块,然后每个版块里调用就可以了,于是你轻轻松松的实现了下面的功能:

代码执行结果:

此时你信心满满的把这个代码提交给你的TEAM LEADER审核,没成想,没过5分钟,代码就被打回来了, TEAM LEADER给你反馈是,我现在有很多模块需要加认证模块,你的代码虽然实现了功能,但是需要更改需要加认证的各个模块的代码,这直接违反了软件开发中的一个原则“开放-封闭”原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

封闭:已实现功能的代码块不应该被修改

开放:对现有功能的扩展开放

这个原则你还是第一次听说,再次感受到了野生程序员和正规军的差距,可是,老大要求的实现要如何实现呢?如何在不该原有功能的情况下加上认证功能呢?你一时想不出来思路,只好带着这个问题回家继续写,媳妇不在家,去隔壁老王家串门了,你正好落得清静,一不小心就想到了解决方案,不改源代码可以实现啊。

你肯定知道什么是高阶函数,就是把一个函数当作一个参数传递给另一个函数,那么,我只需要写一个认证方法,每次调用需要验证的功能时,直接把这个函数当作一个参数传给这个验证模块不就行了吗?哈哈,机智如我,于是你啪啪啪改写了之前的代码:

你明白我想传达什么意思了么? 你:......好像不太明白。 老王:好吧,那我在给你点一下,你之前写的下面这段调用认证的代码

login(America)

login(Beijing)

你之所以修改了调用方式,是因为用户每次调用时需要执行login(America)类似的,其实只需要稍微改改就可以了:

America = login(America)

Beijing = login(Beijing)

这样你,其它人调用Beijing时,其实相当于调用了login(Beijing),通过login里的验证后,就会自动调用Beijing功能。 你:我擦,还真是唉。。。,老王,还是你nb。。。不过,等等,我这样写了好,那用户调用时,应该是下面这个样子:

America = login(America)

Beijing = login(Beijing)

America()

Beijing()

那么此时问题又来了,America = login(America)即先执行完右边的login(America)然后把结果赋值给了左边的America,即你还没有调用,程序自己就执行了,那么这个代码应该等我用户登陆的时候才执行对吧,不信我试给你看:

运行结果如下:

请输入你的姓名:

果然老王说的是对的,根本就没有去调用,这个代码就自动执行了,这时你说:这个问题应该怎么解决呢?老王问:你知道嵌套函数吗?你回答:知道。老王说:想实现一开始你写的america = login(america)不触发你函数的执行,只需要在这个login里面再定义一层函数,第一次调用america = login(america)只调用到外层login,这个login虽然会执行,但不会触发认证了,因为认证的所有代码被封装在login里层的新定义 的函数里了,login只返回 里层函数的函数名,这样下次再执行america()时, 就会调用里层函数啦,你说:...这是什么意思?我一脸懵逼。老王说:算了,还是给你看代码吧:

让我们来看一下代码的执行顺序:

1.程序直接到了最下边America = login(America),调用了login,并把函数当作参数传递进去;

2.在login函数内部,程序从上到下执行,首先执行inner(),在inner()函数里做了认证;

3.inner()函数执行完,返回inner函数,此时return inner即代表返回的是inner函数的内存地址;

4.在代码的底部,America = login(America),login(America)返回的是inner的内存地址;

5.我们刚刚说:如果打印的是函数名,则是函数的内存地址,如果函数名加上(),那么就是执行函数;

6.所以America = login(America)此时左边的America代表的是inner函数的内存地址,我们打印后可以看到;

7.America()执行innera函数;

此时问题暂时得到解决,在刚刚的代码中,有人会说,returni inner表示返回一个值代表着程序的终止,那么为什么我还能运行America()呢?那么我们就需要了解一下闭包了:

闭包

关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问他们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含他们外部函数之外被调用时,就会形成闭包。

也就是说:内部函数会在外部w函数执行后返回。而当这个函内部函数执行时,它仍然必须访问其外部函数的局部、参数以及其他内部的函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回的值,但也会受到内部函数的影响。

代码如下:

在函数里又定义了一层子函数,子函数被返回了,就是在外层函数执行的时候,返回了子函数的内存地址,在外面执行子函数的时候,又引用了外层函数这个变量,这种现象称之为:闭包。

等等,刚刚视频网站还没有完结......

你仔细看了老王的代码,觉得老王真的不是一般人呀,这种姿势很牛逼呀,你独创的吗?

此时你的媳妇嗤嗤的笑出声来,你也不知道她笑个球

老王说:呵呵,这不是我独创的,这是开发中一个常见的语法,叫语法糖,官方名称“装饰器”,其实上面的语法,还可以更简单:

效果是一样的。

你开心的玩着老王交给你的新姿势,玩着玩着就给你的北京专区模块加了个参数,然后,结果就出错了...

你说:老王,怎么传递一个参数就不行了呢?

老王回答:那是肯定的呀,你调用Beijing的时候,其实是相当于调用的login,你的Beijing第一次调用时Beijing = login(Beijing),返回的是inner的内存地址,第二次用户自己调用Beijing("3P"),实际上相当于调用的是inner,但你的inner定义的时候并没有设置参数,但你给它传递了一个参数,所以这就报错了,你说对不对?

你说:但是我的版块需要传参数啊,你不让我传不行啊!

老王回答:没说不让你传,稍作改动即可!

运行结果如下:

那么,问题又来了,我有的版块没有其他的频道,那么我就不用传递参数,但是如果不传递参数就会报错,怎么办?这时候我们绞尽脑汁,传一个参数可以,不传参数也可以,眼睛一亮,非固定参数!

运行结果如下:

老王:你再试试就可以了

你:果然好使,大神就是大神啊......

老王说:这种姿势多练练,我就先回去了

你的媳妇为了不让打扰你,提出去她的好姐妹家过夜,你觉得你的媳妇真体贴,最终你搞定了所有需求,完全遵守封闭-开放原则,此时你累的已经不行了,洗洗就抓紧睡了,半夜,上厕所,隐隐听到隔壁老王家有微弱的女人的声音传来,你会心一笑,老王这家伙,不声不响找了女朋友也不带给我看看,改天一定要见下真人。

第二2天早上,产品经理又提了新的需求,要允许用户选择用qq\weibo\weixin认证,此时的你,已深谙装饰器各种装逼技巧,轻松的就实现了新的需求。

代码运行结果如下:

希望对你有帮助

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180509A1SY5O00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券