修饰器模式

无论何时我们想对一个对象添加额外的功能,都有下面这些不同的可选方法。1.如果合理,可以直接将功能添加到对象所属的类(例如,添加一个新的方法)2.使用组合3.使用继承
与继承相比,通常应该优先选择组合,因为继承使得代码更加难以复用,继承关系是静态的,并且应用于整个类以及这个类的所有实例。
设计模式为我们提供第四种可选方法,以支持动态地(运行时)扩展一个对象的功能,这种方法就是修饰器(装饰器)。修饰器模式能够以透明的方式(不会影响其他对象)动态地将功能添加到一个对象中。
在许多编程语言中,使用子类化(继承)来实现修饰器模式。在Python中,我们可以(并且应该)使用内置的修饰器特性。一个Python修饰器就是对Python语法的一个特定改变,用于扩展一个类、方法或函数的行为,而无需使用继承。从实现的角度来说,Python修饰器是一个可调用对象(函数,方法或类),接受一个函数fin作为输入,并返回另一个函数对象fout。这意味着可以将任何具有这些属性的可调用对象当做一个修饰器。注意:修饰器模式与Python修饰器之间并不是一对一的等价关系。Python修饰器能做的实际上比修饰器模式多得多,其中之一就是实现修饰器模式。现实生活中的例子:修饰器模式用于扩展一个对象的功能。这类扩展的实际例子有,给枪加一个消音器、使用不同的照相机镜头等。应用案例:当用于实现横切关注点时,修饰器模式会大显神威。一般来说,应用中有些部件是通用的,可应用于其他部件,这样的部件被看作横切关注点。

#我们知道,使用递归算法实现斐波那契数列,直接了当,但性能问题较大。
#如下:
def fib(n):assert (n>=0),'n must be >=0'return n if n in (0,1) else fib(n-1)+fib(n-2)if __name__ == '__main__':from timeit import Timert = Timer('fib(8)','from __main__ import fib')print(t.timeit())#耗时 9.770086538557482 s

#使用memoization的方法试着改善
known = {0:0,1:1}def fib(n):assert (n>=0),'n must be >=0'if n in known:return known[n]res =  fib(n-1)+fib(n-2)known[n]=resreturn resif __name__ == '__main__':from timeit import Timert = Timer('fib(100)','from __main__ import fib')print(t.timeit())#耗时 0.16005969749279084 s

#执行基于memorization的代码实现,可恶意看到性能得到了很大的提升,甚至对于计算大的数值也是可以接受的。但这方法有一个问题,虽然性能不再是一个问题,但是代码却没有不使用memorization时那么简洁。
#如果我们想要扩展代码,加入更多的数学函数,将其转变成一个模块,那又会是什么样的的呢?假设决定加入的下一个函数是nsum(),该函数返回前n个数字的和。
#使用memeorization实现nsum()函数的代码如下:
known_sum = {0:0}def nsum(n):assert (n>0), 'n must be >= 0'if n in known_sum:return known_sum[n]res = n + nsum(n-1)known_sum[n] = resreturn res

我们发现新增一个函数多了一个名为known_sum的新字典,为nsum提供缓存作用,并且函数本身也不比使用memorization时的更复杂。
这个模块逐步变得不必要的复杂。操持函数与朴素版本一样的简单,但在性能上又能与使用memorization的函数接近,这可能吗?
幸运的是确实可能,解决方案就是使用修饰器模式。
#创建一个memorize()函数,其接受一个函数fn作为输入,使用名为know的字典作为缓存。如下:
import functools
def memoize(fn):known = {}@functools.wraps(fn)def memoizer(*args):if args not in known:known[args] = fn(*args)return known[args]return memoizer

#现在对朴素版本应用memoize()修饰器。这样既能保持代码的可读性又不影响性能。我们通过修饰来应用一个修饰器。修饰使用@name语法,其中name是指我们想要使用的修饰器名称。
import functools
def memoize(fn):known = {}@functools.wraps(fn)def memoizer(*args):if args not in known:known[args] = fn(*args)return known[args]return memoizer@memoize
def fib(n):assert (n>=0),'n must be >=0'return n if n in (0,1) else fib(n-1)+fib(n-2)@memoize
def nsum(n):assert (n>=0),'n must be >=0'return 0 if n==0 else n+nsum(n-1)if __name__ == '__main__':from timeit import Timermeasure = [{'exec':'fib(100)','import':'fib','func':fib},{'exec':'nsum(200)','import':'nsum','func':nsum}]for m in measure:t = Timer('{}'.format(m['exec']),'from __main__ import {}'.format(m['import']))print('name:{},doc:{},executing:{},time:{}'.format(m['func'].__name__,m['func'].__doc__,m['exec'],t.timeit()))#name:fib,doc:None,executing:fib(100),time:0.18119357924100937
#name:nsum,doc:None,executing:nsum(200),time:0.1972677136059823

小结

我们使用修饰器模式来扩展一个对象的行为,无需使用继承,非常方便。修饰器模式是实现横切关注点的绝佳方案,因为横切关注点通用但不太适合使用面向对象编程范式来实现。

转载于:https://www.cnblogs.com/xiaoshayu520ly/p/10983861.html

结构型模式---修饰器模式相关推荐

  1. Java设计模式之结构型:装饰器模式

    一.什么是装饰器模式: 当需要对类的功能进行拓展时,一般可以使用继承,但如果需要拓展的功能种类很繁多,那势必会生成很多子类,增加系统的复杂性,并且使用继承实现功能拓展时,我们必须能够预见这些拓展功能, ...

  2. 第六天:结构型模式--修饰器模式

    ####零.修饰器模式 当我们相对一个对象添加新功能的时候,无非就是下面这三种方法: 直接修改对象所属类 使用组合 使用继承 这里我们会优先考虑使用直接修改对象类,如果行不通我们会使用组合,最次的情况 ...

  3. Java设计模式之结构型:享元模式

    一.什么是享元模式: 享元模式通过共享技术有效地支持细粒度.状态变化小的对象复用,当系统中存在有多个相同的对象,那么只共享一份,不必每个都去实例化一个对象,极大地减少系统中对象的数量.比如说一个文本系 ...

  4. python模式选择符,Python设计模式之修饰器模式

    无论何时我们想对一个对象添加额外的功能,都有下面这些不同的可选方法. 如果合理,可以直接将功能添加到对象所属的类(例如:添加一个新方法) 使用组合 使用继承 与继承相比,通常应该优先选择组合,因为继承 ...

  5. 结构型设计模式之组合模式

    结构型设计模式之组合模式 组合模式 应用场景 优缺点 主要角色 组合模式结构 分类 透明组合模式 创建抽象根节点 创建树枝节点 创建叶子节点 客户端调用 安全组合模式 创建抽象根节点 创建树枝节点 创 ...

  6. python 修饰器 教程_python 实现 修饰器模式

    无论何时我们想对一个对象添加额外的功能,都有下面这些不同的可选方法. 如果合理,可以直接将功能添加到对象所属的类(例如,添加一个新的方法) 使用组合 使用继承 与继承相比,通常应该优先选择组合,因为继 ...

  7. python修饰器_python设计模式之修饰器模式

    python设计模式之修饰器模式 无论何时我们想对一个对象添加额外的功能,都有下面这些不同的可选方法. [ ] 如果合理,可以直接将功能添加到对象所属的类(例如,添加一个新的方法) [ ] 使用组合 ...

  8. 修饰器模式(day04)

    修饰器设计模式 --最近我给女朋友买了一款可以更换外壳的手机.现在的外壳是红色的,假如我想用这款手机的时候,会更换成银灰色的外壳.但是我不能随意更换天线或者话筒,因为这些功能模块在手机生产的时候就已经 ...

  9. php装饰器模式 简书,装饰器模式/包装器模式

    在电视剧<相爱十年>中,主角肖然把出现了品控问题的安尔雅肥皂换了个包装.改了套说辞变成了全新的品牌浴雪清,并成功的推销出去了,得到了第一桶金.这就说所谓的包装,本质上并没有改变,但是外在表 ...

最新文章

  1. 样式超出设定宽度显示显示省略号
  2. 统计ES性能的python脚本
  3. Unity 中使用Async-Await替代 coroutines
  4. 手动添加linux用户,Linux入门教程:如何手动创建一个Linux用户
  5. 【渝粤题库】陕西师范大学202411 管理学基础 作业 (专升本、高起本、高起专)
  6. OpenCV学习笔记:绘图指令(矩形、圆、线、文本标注)
  7. Membership Leakage in Label-Only Exposures论文解读
  8. 深蓝儿童破解2012世界末日预言(转)
  9. SAP Business One(SAP B1):供应商和客户如何分类
  10. Spring Boot项目WebService接口发布、调用、以及常见错误详解
  11. RT-Thread学习笔记【ADC与DAC设备】
  12. 利用Wifidog实现微信wifi连接以及自写认证服务器
  13. 鸡尾酒排序和冒泡优化
  14. MacOs 12 微信闪退
  15. linux系统应用学习(三)--- 动态库静态库
  16. java jbutton添加图片代码_java-如何将图像添加到JButton
  17. 2021 第二届天翼杯ctf
  18. 魔术表演-第14届蓝桥杯省赛Scratch中级组真题第1题
  19. 腾讯云安装docker
  20. [龙讯6号]龙芯2E首次公布设计细节

热门文章

  1. BBC纪录片《化学史》,看看历史上化学家们的脑洞有多大?
  2. ABC084-C-Special Trains
  3. python实现将一幅图拼接到另一幅图上
  4. 《蜀山云游记》——君山七十二螺
  5. 《基于Python的大数据分析基础及实战》第二章
  6. DML语句--更新(update)
  7. 看安全卫士360是如何利用数字激励用户行为的?
  8. MySQL(解压版)下载、安装与配置
  9. 超简单集成!手把手教你实现音频编辑能力
  10. 如何在米拓的metinfo的模板中加入自己定义的样式