转载文章链接:
有过开发经验得朋友队装饰模式这个词应该不陌生, 装饰装饰, 顾名思义就是指对我们原来有得东西进行装饰, 比如我们买了新房, 那么我们对毛坯房的装修, 就是对我们房子进行拓展, 让它更加完善! 同样得对于代码也是如此, 我们就是对我们原有的功能进行功能的拓展!
一, 假设我们有下面的一个场景
1, 假设你现在是某个公司的工程师, 你当前负责得事情就是公司某个项目整个系统架构基本功能得开发, 后续会有很多同事会直接调用你写得功能函数. 于是你就风风火火得一顿狂撸, 终于有了下面代码:
- def test1():
- print("==== 给员工加薪了!====")
- def test2():
- print("==== 出新版本, 杀一个程序员祭天!====")
很好, 同事们调得很舒服, 系统跑得很好, 老板赚钱...
2, 某一天, 老板突然来找你, 说有个员工莫名奇妙被加薪了, 并不是老板自己加的, 老板说, 加薪这里要验证下, 只有我才有权限给员工加薪. 又说最近程序员急剧减少, 被杀得太多了, 也要加权限, 只有老板才能杀!
3, 作为一个出色得程序员, 这点要求当然没问题, 于是你又一顿加班加点狂改, 把老板的需求都是实现了, 于是又有了下面的代码:
- def test1():
- if ROLE == "boss":
- print("==== 给员工加薪了!====")
- else:
- print("==== 不好意思, 您不是老板 ====")
- def test2():
- if ROLE == "boss":
- print("==== 出新版本, 杀一个程序员祭天!====")
- else:
- print("==== 不好意思, 您不是老板 ====")
4, 这一切看起来很正常, 但是我们不妨假设下, 如果有 10000 个类似得功能函数需要添加类似得验证, 又或者添加其它的功能呢? 我们姑且假设你单身多年得手速非常快, 但是这样搞法也是很浪费时间的吧? 更何况贸然得去改动原来得代码, 风险也是不小的, 所以我们换下面这种方式看看!
二, 闭包得引入
1, 之前我已经分享过闭包的学习笔记了, 不了解的可以去看下, 闭包得几个条件如下: 一个函数内定义了内函数, 并且内涵是使用了外函数的局部变量, 最后外函数返回了内涵的引用. 满足了这三个条件我们就可以称之为闭包.
2, 那么我们到底引入闭包做什么呢? 闭包又是如何解决我们上面的问题得呢? 我们先看下下面这段代码:
- def iwapper(fun):
- inner():
- if ROLE == "boss":
- print("==== 身法验证通过 ====")
- fun()
- else:
- print("==== 不好意思, 您不是老板 ====")
- return inner
- def test1():
- print("==== 给员工加薪了!====")
- def test2():
- print("==== 出新版本, 杀一个程序员祭天!====")
- # 调用 iwapper, 并传入 test1 函数的引用, 给员工加薪
- addMoney = iwapper(test1)
- addMoney ()
- # 调用 iwapper, 并传入 test2 函数的引用, 杀程序员
- killDev = iwapper(test2)
- killDev()
打印下结果:
此时, 我们产生了两个新函数, 一个是 addMoney(), 另一个是 killDev(), 调用这两个即实现了添加验证得功能, 保持了原来功能函数不用修改, 并且大大减少了代码得冗余, 但是另一个问题又来了, 代码得调用方式变了, 我们需要告知其他所有人, 修改他们得代码! 很显然这是一个更糟糕得做法!
3, 路总是一步步走出来得, 经历了那么多波折, 真相也越来越接近了, 聪明得你很快又拿出了下面得方案:
- def iwapper(fun):
- inner():
- if ROLE == "boss":
- print("==== 身法验证通过 ====")
- fun()
- else:
- print("==== 不好意思, 您不是老板 ====")
- return inner
- def test1():
- print("==== 给员工加薪了!====")
- def test2():
- print("==== 出新版本, 杀一个程序员祭天!====")
- # 调用 iwapper, 并传如 test1 函数的引用, 给员工加薪
- test1 = iwapper(test1)
- test ()
- # 调用 iwapper, 并传如 test2 函数的引用, 杀程序员
- test2 = iwapper(test2)
- test2()
打印下结果:
这里我们可以看到我们并没有对原有功能函数进行修改, 并且提供给外部调用得方式依然是 test1(),test2(), 已经满足了我们得条件了, 那么我们一步步的分析下:
1 > 首先定义了一个 test1() 函数的时候, 就相当于再内存中创建了一块内存区域, 我们姑且叫它 A. 同样得我们在定义 iwapper(fun) 的时候, 在其内部定义了 inner() 函数, 我们姑且把它所指向的内存区域命名为 B.
2 > 紧接着, 当我们调用 iwapper(test1) 的时候, 我们把 test1() 的引用传了进去, 所以 fun 此时指向了 test1() 的引用, 也就是指向了 A
3 > 接下来我们使用 test1 接收了 iwapper(fun) 得返回值, 而 iwapper(fun) 返回得是 inner() 的引用, 所以此时 test1 指向了 B
4 > 所以最后我们调用 test1() 函数得时候其实是调用了 inner(), 而 inner() 函数除了进行校验功能之外还调用了 fun() 函数, 从第二点我们可以知道, fun 指向了 A 也就是最开始定义得 test1(), 这样我们即进行了验证功能, 又对原来功能进行调用!
三, 装饰器
1, 说了那么多, 我们来看看如果遇到上面的需求, 如果使用 Python 装饰器来实现的话会怎么样? 看下面代码:
- def iwapper(fun):
- inner():
- if ROLE == "boss":
- print("==== 身法验证通过 ====")
- fun()
- else:
- print("==== 不好意思, 您不是老板 ====")
- return inner
- @iwapper
- def test1():
- print("==== 给员工加薪了!====")
- @iwapper
- def test2():
- print("==== 出新版本, 杀一个程序员祭天!====")
那么看下效果:
很明显, 效果实现了, 这时候也许就又朋友要说道说道了, nmmp,Python 解析器那么简单, 你前面说那么多废话干嘛, 直接一开始就来这段不就行了? 朋友莫激动, 先把刀放下, 其实如果我们单看最后面的代码, 我们仅仅知道 @iwapper 是 python 装饰器的实现方式而已, 但是我们并不知道这句代码到底干了什么事, 而我们前面说的就是 python 解析器的实现原理了! 其中得思想获取会对我们有所帮助也不一定呢!
总结: 这里仅仅是对 Python 中装饰器得最简单使用方式进行学习, 更重要得是学习 Python 决绝类似问题的思想! 其更多用法大家可以查阅相关得资料, 比如说带参数的装饰器, 多个装饰器一起使用等等!
----------------
来源: http://www.bubuko.com/infodetail-3302285.html