我们先来解释一下AOP的概念。
AOP 即面向切面编程,指扩展功能不修改源代码,将功能代码从业务逻辑代码中分离出来。
主要作用就是将类似于日志记录,性能统计,安全控制,事务处理,异常处理等重复性的代码块从业务逻辑代码中划分出来,对这些行为的分离。并且将它们独立到非知道业务逻辑的方法中,从而做到改变这些行为的时候不影响业务逻辑代码。
我们python中使用装饰器实现AOP。装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,装饰器的作用就是为已经存在的对象添加额外的功能。
装饰器 本身也是一个函数 ,这个函数以闭包的形式定义,在使用这个装饰器函数时,在被装饰的函数的前一行,使用 @装饰器函数名 形式来装饰。
闭包是指在一个函数中定义了一个另外一个函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。 闭包的使用,可以隐藏内部函数的工作细节,只给外部使用者提供一个可以执行的内部函数的引用。
写一个功能函数,实现一百万次的累加
def my_count():
s = 0
for i in range(1000001):
s += i
print('sum : ', s)
装饰器实现
import time
def count_time(func):
def wrapper(): #wrapper 装饰
start = time.time()
func()
end = time.time()
print('共计执行:%s 秒'%(end - start)) # 使用%d显示,取整后是0秒,因为不到一秒
return wrapper
@count_time # 这实际就相当于解决方法3中的 my_count = count_tiem(my_count)
def my_count():
s = 0
for i in range(10000001):
s += i
print('sum : ', s)
my_count()
这样实现的好处是,定义好了闭包函数后。只需要通过 @xxx 形式的装饰器语法,将 @xxx 加到要装饰的函数前即可。使用者在使用时,根本不需要知道被装饰了。只需要知道原来的函数功能是什么即可。
在执行 @xxx 时 ,实际就是将 原函数传递到闭包中,然后原函数的引用指向闭包返回的装饰过的内部函数的引用。
在类中通过使用 init 和 __call__方法来实现
class Test(object):
# 通过初始化方法,将要被装饰的函数传进来并记录下来
def __init__(self, func):
self.__func = func
# 重写 __call__ 方法来实现装饰内容
def __call__(self, *args, **kwargs):
print('wrapper context')
self.__func(*args, **kwargs)
# 实际通过类的魔法方法call来实现
@Test # --> show = Test(show) show由原来引用函数,装饰后变成引用Test装饰类的对象
def show():
pass
show() # 对象调用方法,实际上是调用魔法方法call,实现了装饰器
一个函数在使用时,通过一个装饰器来扩展,可能并不能完成达到预期。
Python 中允许一个函数被多个装饰器所装饰。
# 装饰器1
def setFunc1(func):
def wrapper1(*args, **kwargs):
print('Wrapper Context 1 Start...')
func(args, kwargs)
print('Wrapper Context 1 End...')
return wrapper
# 装饰器2
def setFunc2(func):
def wrapper2(*args, **kwargs):
print('Wrapper Context 2 Start...')
func(args, kwargs)
print('Wrapper Context 2 End...')
return wrapper
#一个函数被装饰了两次
@setFunc1
@setFunc2
def show(*args, **kwargs):
print('Show Run ...')
show()
执行结果
Wrapper Context 1 Start...
Wrapper Context 2 Start...
Show Run ...
Wrapper Context 2 End...
Wrapper Context 1 End...
这个装饰器是从上到下执行,从下向上装饰,
在web框架中的钩子函数和中间件都有使用。