装饰器模式是一种面向对象的设计模式,可以不改变对象类型而灵活扩展对象的功能或责任,本文以 Python3 为准,介绍了 Python 对装饰器的支持及其经典用法。
Python 语法中的 @ 支持直接使用装饰器,可以非常方便的实现各种功能。例如:
- 函数返回值监控
- 函数性能监控
- 自动生成比较方法
- 插入日志
- 性能测试
- 事务处理
- 缓存和权限验证
一个简单的例子
通过一个简单的例子解释装饰器中的概念:对一段文本 “hello world” 进行渲染,例如加黑、加斜体。
- 闭包
- 要理解 Python 的装饰器,首先要理解闭包,闭包的特点就是内部函数引用了外部函数中的变量。
- 在 Python 中,支持将函数当做对象使用,也就是可以将一个函数当做普通变量一样用作另一个函数的参数和返回值。
- 闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,知道内部函数被调用结束。
- 可以通过
函数名.__closure__
判断是否是闭包函数,在函数是闭包函数时,返回一个 cell 元素;不是闭包时,返回 None。
- 利用闭包实现
# 加黑装饰器
def renderbold(func):
def wrapped():
return "<b>" + func() + "</b>"
return wrapped
# 加斜体装饰器
def renderitalic(func):
def wrapped():
return "<i>" + func() + "</i>"
return wrapped
print(renderbold(renderitalic(helloworld))())
## 返回 <b><i>hello world</i></b>
- 语法糖,通过 @ 运算符直观方便的对函数进行装饰
# 自下而上调用装饰器
@renderbold
@renderitalic
def helloworld():
return "hello world"
print(helloworld())
## 返回 <b><i>hello world</i></b>
带参数的装饰器
想要更灵活的使用装饰器,可以通过传递参数的形式,控制装饰器,例子如下:
def get_parameter(*args,**kwargs): # 工厂函数,用来接受参数
def log_time(func):
def make_decorater():
print(args,kwargs)
print('现在开始装饰')
func()
print('现在结束装饰')
return make_decorater
return log_time
@get_parameter('装饰器参数')
def test():
print('我是被装饰的函数')
test()
类的装饰器
- 类的装饰器是作用在类上面,只会执行一次,对该类的所有实例(对象)都会产生影响。
functools.total_ordering
类装饰器提供了一个工具去生成缺少的比较方法,是最经典的例子。- 比较运算符一共 6 个,
@total_ordering
可以帮忙补齐除了==,!=
的剩余 4 个,下面是实现代码。
def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
# 所有运算符的实现(Python3 源码中并没有使用 lambda,这里为了简洁而使用 lambda 代替)
convert = {
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
('__le__', lambda self, other: self < other or self == other),
('__ge__', lambda self, other: not self < other)],
'__le__': [('__ge__', lambda self, other: not self <= other or self == other),
('__lt__', lambda self, other: self <= other and not self == other),
('__gt__', lambda self, other: not self <= other)],
'__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
('__ge__', lambda self, other: self > other or self == other),
('__le__', lambda self, other: not self > other)],
'__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
('__gt__', lambda self, other: self >= other and not self == other),
('__lt__', lambda self, other: not self >= other)]
}
# 找到已经定义过的运算符
roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)]
if not roots:
raise ValueError('must define at least one ordering operation: < > <= >=')
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
print('choosed:' + root)
# 把其他运算符的实现加入到类中
for opname, opfunc in convert[root]:
if opname not in roots:
opfunc.__name__ = opname
# 拷贝 int 类型的说明
opfunc.__doc__ = getattr(int, opname).__doc__
setattr(cls, opname, opfunc)
return cls
@total_ordering
class Name():
def __init__(self, fn, ln):
self.first_name = fn
self.last_name = ln
def __eq__(self, other):
print('=== my eq===')
return (self.first_name, self.last_name) == (other.first_name, other.last_name)
def __gt__(self, other):
print('=== my gt===')
return (self.first_name, self.last_name) > (other.first_name, other.last_name)
a = Name('Jing', 'Guo')
b = Name('Rong', 'Huang')
print(a == b)
print(a != b)
print(a > b)
print(a < b)
print(a <= b)
print(a >= b)
# 结果
choosed:__gt__
=== my eq===
False
=== my eq===
True
=== my gt===
False
=== my gt===
=== my eq===
True
=== my gt===
True
=== my gt===
=== my eq===
False
参考文档
https://blog.csdn.net/emaste_r/article/details/78113741
https://www.jianshu.com/p/ab702e4d4ba7
https://www.jianshu.com/p/f74624e43226
https://blog.csdn.net/zwj1452267376/article/details/88411889