第二个值得学习的结构模式是装饰器模式,它允许程序员以透明的方式(影响其他对象)动态地给对象增加能力。
可以用Pythonic的方式来写装饰器(意味着使用语言的特性),这要归功于内置的装饰器特性(https://docs.python.org/3/reference/compound_stmts.html#function)。
Python 装饰器是一个可调用的(函数、方法或类),它获得一个函数对象 func_in 作为输入,并返回另一个函数对象 func_out。它用于扩展函数、方法或类的行为。
真实世界的例子
装饰器模式通常用于扩展对象的功能。在日常生活中,这种扩展的例子有:在枪上加一个消音器,使用不同的相机镜头等等。
Django框架中有大量装饰器
- 限制某些HTTP请求对视图的访问
- 控制
- 按单个视图控制压缩
- 基于特定HTTP请求头控制缓存
Pyramid框架和Zope应用服务器也使用装饰器来实现各种目标。
- 将函数注册为事件订阅者
- 以特定权限保护一个方法
- 实现适配器模式
应用
装饰器模式在跨领域方面大放异彩:
- 数据验证
- 缓存
- 日志
- 监控
- 调试
- 业务规则
- 加密
使用修饰器模式的另一个常见例子是(Graphical User Interface,GUI)工具集。在GUI工具集中,我们希望能够将一些特性,比如边框、阴影、颜色以及滚屏,添加到组件/控件。
实例
所有的递归函数都可以从缓存中受益,所以让我们尝试返回前n个数字之和的函数number_sum()。
def number_sum(n):
'''Returns the sum of the first n numbers'''
assert(n >= 0), 'n must be >= 0'
if n == 0:
return 0
else:
return n + number_sum(n-1)
if __name__ == '__main__':
from timeit import Timer
t = Timer('number_sum(30)', 'from __main__ import number_sum')
print('Time: ', t.timeit())
执行输出耗时:Time: 34.952999532999456
下面的代码中,我们使用dict来缓存已经计算好的和。我们还改变了传递给number_sum()函数的参数。我们想计算前300个数字的和,而不是只计算前30个。
sum_cache = {0:0}
def number_sum(n):
'''Returns the sum of the first n numbers'''
assert(n >= 0), 'n must be >= 0'
if n in sum_cache:
return sum_cache[n]
res = n + number_sum(n-1)
# Add the value to the cache
sum_cache[n] = res
return res
if __name__ == '__main__':
from timeit import Timer
t = Timer('number_sum(300)', 'from __main__ import number_sum')
print('Time: ', t.timeit())
执行输出耗时:Time: 1.2133596080002462。快了但是单码复杂了,且不方便复用。改用lru_cache装饰器会更清晰:
from functools import lru_cache
@lru_cache
def number_sum(n):
'''Returns the sum of the first n numbers'''
assert(n >= 0), 'n must be >= 0'
if n == 0:
return 0
else:
return n + number_sum(n-1)
if __name__ == '__main__':
from timeit import Timer
t = Timer('number_sum(30)', 'from __main__ import number_sum')
print('Time: ', t.timeit())
网友评论