装饰器模式(Decorator Pattern)
定义
“Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)”
Excerpt From: 秦小波 著. “设计模式之禅(第2版)(华章原创精品).”
UML 示例
@startuml
class Component
class ConcreateComponent
class Decorator
class ConcreateDecorator
Component <|-- ConcreateComponent
Component <|-- Decorator
Decorator <-- ConcreateDecorator
@enduml
Component是基本组件,而ConreateComponent是具体的组件,但为了对具体的组件增加一些额外的配件,则需要用到ConreateDecorator。
例如:一把枪,就是基础组件,而对于消音器等就是他的配件。
代码实现
在精通python设计模式中,关于适配器模式的介绍,实现也是相当简单的,只要实现目标接口方法,
'''
本样例主要说明python装饰器模式的简单应用
首先明确:python的装饰器所处理的场景要大于装饰器模式,此处只做简单装饰器模式的样例
'''
from functools import wraps
def save_result(fn):
know_result = dict()
@wraps(fn)
def decorator(*args):
if args in know_result:
return know_result[args]
know_result[args] = fn(*args)
return know_result[args]
return decorator
@save_result
def fibonacci(n):
'''
执行斐波那切数列
'''
return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
if __name__ == "__main__":
print(fibonacci(8))
- 上述例子中是如何使用的装饰器模式,在哪里体现了?
首先正常的fibonacci数列,每次执行都是从最高值一直算到最小值退出,为了减小运算时间成本,故引入一段代码,存储已经计算过的fibonacci值,判断如果值已经存在则返回存在的值。
而@符号这个语法糖就是python的装饰器,用来让save_result函数修饰fibonacci函数
知识点
- 重点理解闭包是什么
- 闭包定义:闭包指延伸了作用域的函数,其中包含函数定义体中引用、但不是定义在定义体中的非全局变量
如何理解这句话?以上述例子为例,save_result中的decorator就是延伸了作用域的函数,known_result就是定义体中的引用,但不在定义体中定义的非全局变量。所以满足上述条件就称decorator是闭包函数。
- 自由变量:不在闭包中定义但在闭包中引用的非全局变量,例如:know_result
- @语法糖
- 应用场景:
数据校验
事务处理(这里的事务类似于数据库事务,意味着要么所有步骤都成功完成,要么事务失败)
缓存
日志
监控
调试
业务规则
压缩
加密
扩展阅读之python装饰的几种类型
我理解的装饰器类型包括:装饰函数的装饰器,带参数的装饰器,装饰类的装饰器,装饰类的带参数的装饰器,类形式的装饰器、类形式的带参数的装饰器
- 装饰函数的装饰器:最简单的一种,上述装饰器模式使用的就是此种类型。
- 带参数的装饰器:如果对闭包理解够深刻,应该不难发现,闭包函数通过访问外部函数的变量从而进行一些操作。那么带参数的装饰器无非就是再增加一层嵌套,而这层嵌套需要增加传参。
def logger(*args, **kwargs):
if 'level' in kwargs:
level = kwargs['level']
def decorator_func(fn):
@wraps(fn)
def decorator(*args, **kwargs):
logging.log(level, 'excute')
return fn(*args, **kwargs)
return decorator
return decorator_func
@logger(level=logging.CRITICAL)
def add(a, b):
return a+b
- 类装饰器
类装饰器顾名思义就是用来装饰类的,所以说类装饰器是通过@decorator装饰class的。
def single(cls):
instance = None
@wraps(cls)
def decorator(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance
else:
return instance
return decorator
total_count = 0
def count(cls):
@wraps(cls)
def decorator(*args, **kwargs):
global total_count
total_count += 1
return cls(*args, **kwargs)
return decorator
@count
@single
class Test:
pass
if __name__ == "__main__":
# print(fibonacci(8))
t1 = Test() # <__main__.Test object at 0x10e0e1400>
t2 = Test() # <__main__.Test object at 0x10e0e1400>
t3 = Test() # <__main__.Test object at 0x10e0e1400>
print('test create count {}'.format(total_count)) #test create count 3
print(t1)
print(t2)
print(t3)
if t1 is t2 is t3:
print('t1==t2==t3') # t1==t2==t3
- 带参数的装饰类的装饰器
# 带参数装饰器
def issingle(flag=True):
def single(cls):
instance = None
@wraps(cls)
def decorator(*args, **kwargs):
nonlocal instance
if flag is False:
instance = cls(*args, **kwargs)
else:
if instance is None:
instance = cls(*args, **kwargs)
return instance
return decorator
return single
@issingle(True)
class T1:
pass
@issingle(False)
class T2:
pass
if __name__ == "__main__":
t4 = T1()
t5 = T1()
print(t4) # <__main__.T1 object at 0x105482be0>
print(t5) # <__main__.T1 object at 0x105482be0>
t6 = T2() # <__main__.T2 object at 0x105482c18>
t7 = T2() # <__main__.T2 object at 0x105482c18>
print(t6)
print(t7)
if t4 is t5:
print('t4 == t5') ## t4 == t5
if t6 is t7:
print('t6 == t7')
- 类形式的装饰器
类形式的装饰器,重点要理解的就是函数也是类,而类也可以是函数,只要类实现了__call__方法,便是可调用的类。
class Single:
def __init__(self):
super().__init__()
def __call__(self, cls):
instance = None
@wraps(cls)
def decorator(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance
else:
return instance
return decorator
@Single() # 创建一个类对象执行__call__方法
class T3:
pass
t8 = T3()
t9 = T3()
if t8 == t9:
print('t8 == t9')
- 类形式的带参数的装饰器
类形式的装饰器的带参数形式就没有想象的那么难啦,通过__init__函数传参,简洁
class Single:
def __init__(self, flag):
self.flag = flag
super().__init__()
def __call__(self, cls):
instance = None
@wraps(cls)
def decorator(*args, **kwargs):
nonlocal instance
if self.flag is True:
print('hello world!')
if instance is None:
instance = cls(*args, **kwargs)
return instance
else:
return instance
return decorator
@Single(True)
class T3:
pass
t8 = T3()
t9 = T3()
if t8 == t9:
print('t8 == t9')
# 输出两边helloworld!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。