Python3与C#扩展之 装饰器专栏

终于期末考试结束了,聪明的小明同学现在当然是美滋滋的过暑假了,左手一只瓜,右手一本书~正在给老乡小张同学拓展他研究多日的知识点

1.NetCore装饰器模式

装饰器这次从 开始引入,上次刚讲 ,这次把 也带一波(纯Python方向的可以选择性跳过,也可以当扩展)

其实通俗讲就是,给原有对象动态的添加一些额外的职责(毕竟动不动就改类你让其他调用的人咋办?也不符合开放封闭原则是吧~)

举个简单的例子:(https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/Decorators)

BaseComponent.cs

LoginComponent.cs

默认调用:

如果这时候平台需要添加微信第三方登录,怎么办?一般都是用继承来解决,其实还可以通过灵活的 来解决:(好处可以自己体会)

先定义一个通用装饰器(不一定针对登录,注册等等只要在BaseComponent中的都能用)

现在根据需求添加微信登录:(符合开放封闭原则)

调用:(原有系统该怎么用就怎么用,新系统可以使用装饰器来添加新功能)

结果:

如果再加入QQ和新浪登录的功能就再添加一个V3版本的装饰器,继承当时V2版本的登录即可(版本迭代特别方便)

调用:

结果:

其实还有很多用处,比如原有系统缓存这块当时考虑不到,现在并发来了,已经上线了,原有代码又不太敢大幅度修改,这时候装饰器就很方便的给某些功能添加点缓存、测试、日记等等系列功能

实际场景说的已经很明白了,其他的自己摸索一下吧

2.Python装饰器

那Python怎么实现装饰器呢?小胖问道。

小明屁颠屁颠的跑过去说道,通过闭包咯~(闭包如果忘了,可以回顾一下)

2.1.装饰器引入

来看一个应用场景,以前老版本系统因为并发比较小,没考虑到缓存

在不修改原有代码的前提下咋办?我们参照C#和Java写下如下代码:

小张问道:“怎么也这么麻烦啊,C#的那个我就有点晕了,怎么Python也这样啊?”

小明哈哈一笑道:“人生苦短,我用Python~这句话可不是随便说着玩的,来来来,看看Python的语法糖”:

其实

等价于

小张同学瞪了瞪眼睛,努力回想着以前的知识点,然后脱口而出:“这不是我们之前讲的属性装饰器吗?而且好方便啊,这完全符合开放封闭原则啊!“

小明也愣了愣,说道:”也对哦,你不说我都忘了,我们学习面向对象三大特性的时候经常用呢,怪不得这么熟悉呢“

随后又嘀咕了一句:”我怎么不知道开放封闭原则...“

小张嘲笑道:”这你都不知道?对扩展开放,对已经实现的代码封闭嘛~“

2.2.多个装饰器

小明赶紧扯开话题,”咳咳,我们接下来我们接着讲装饰器"

小张问道,像上面那个第三方登录的案例,想加多少加多少,Python怎么办呢?

小明一笑而过~

现在项目又升级了,要求每次调用都要打印一下日记信息,方便以后纠错,小张先用自己的理解打下了这段代码,然后像小明请教:

小明刚美滋滋的喝着口口可乐呢,看到代码后一不小心喷了小张一脸,然后尴尬的说道:“Python又不是只能装饰一个装饰器,来看看我的代码”:

小张耐心的看完了代码,然后说道:“咦,我发现它装饰的时候是从下往上装饰,执行的时候是从上往下啊?执行的时候程序本来就是从上往下,按照道理应该是从上往下装饰啊?”

小明神秘的说道:“你猜啊~你可以把它理解为寄快递和拆快递

小张兴奋的跳起来了:

装饰器:装快递,先包装里面的物品,然后再加个盒子。执行装饰器:拆快递,先拆外面的包装再拆里面的~简直妙不可言啊

2.3.带参装饰器

小明继续讲述他哥哥的血泪历史:

需求时刻在变,系统使用范围更广了,为了不砸场子,抠门的老板决定每年多花5W在技术研发的硬件支持上,这下子技术部老开心了,想想以前前端只能通过CDN和HTTP请求来缓存,后端只能依赖页面缓存和数据库缓存就心塞,于是赶紧新增加一台Redis的云服务器。为了以后和现在缓存代码得变一变了,需要支持指定的缓存数据库:(如果不是维护别人搞的老项目,你这么玩保证被打死,开发的时候老老实实的工厂模式搞起)

带参数的装饰器一般都是用来记录logo日记比较多,自己开发知道debug模式,生产指定except模式等等

小张很高兴,然后练了练手,然后质问小明道:”你是不是藏了一手!“

代码如下:

2.4.通用装饰器

小明尴尬的笑了下,然后赶紧倾囊相授,定义一个通用的装饰器:(传参数就在外面套一层)

这部分知识如果忘记了可以回顾一下,我们之前讲的函数系列:https://www.cnblogs.com/dotnetcrazy/p/9175950.html

2.5.扩展补充

其实装饰器可以做很多事情,比如强制类型检测等,先看几个扩展:

1.装饰器方法签名的问题

成也装饰器,败也装饰器,来个案例看看,装饰器装饰的函数真的就对原函数没点影响?

发现虽然函数调用时候的名字没有变,但是内部签名却变成了闭包里面的函数名了!

玩过逆向的人都知道,像你修改了apk文件,它看似一样,但签名就变了,得再处理才可能绕过原来的一些自效验的验证措施

这边一样的道理,你写了一个装饰器作用在某个函数上,但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。

里面的 就帮我们干了这个事情(之前讲模块的时候引入了functools,随后讲衍生的时候用了里面的偏函数,这边讲讲 )

上面代码改改:

另外: 有一个重要特征是它能让你通过属性 直接访问被包装函数,eg:

2.装饰器传参的扩展(可传可不传)

3.类中定义装饰器

在类里面定义装饰器很简单,但是你首先要确认它的使用方式。比如到底是作为一个实例方法还是类方法:(别忘记写 和

在涉及到继承的时候。 例如,假设你想让在A中定义的装饰器作用在子类B中。你需要像下面这样写:

也就是说,装饰器要被定义成类方法并且你必须显式的使用父类名去调用它。

你不能使用 ,因为在方法定义时,这个类B还没有被创建。

4.类装饰器

看这个之前,我们先来看看怎么把类当函数一样使用:

重载这些魔法方法一般会改变对象的内部行为。上面这个例子就让一个类对象拥有了被调用的行为。

装饰器函数其实是这样一个接口约束,它必须接受一个 对象作为参数,然后返回一个 对象。

在Python中一般 对象都是函数,但也有例外。只要某个对象重写了 方法,那么这个对象就是callable的

用类来实现呢?我们可以让类的构造函数 接受一个函数,然后重载 并返回一个函数,也可以达到装饰器函数的效果

我们拿之前说的通用装饰器的例子继续说:(一般来说装饰器就定义成方法,然后给需要添加的函数或者类方法添加就基本够用了)

对类进行装饰的测试:(以上一个案例为例)

装饰实例方法的时候容易出现莫名其妙的错误,所以一般加上get方法(反射系列的稍后会讲)

完整写法:(你可以去除 试试)

更多的可以参考如下链接:

详解Python装饰器

将装饰器定义为类

Python中的init()和call()函数

python中装饰器的使用和类装饰器在类中方法的使用

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180717G0NHH600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券