假设我们现在有一个函数foo
,函数如下:
def foo(val1, val2):
val = val1 + val2
print val
现在老板有一个需求,想让你看下这个函数的运行时间,那么你能怎么做呢?可能这样?
def foo(val1, val2):
import time
startTime = time.time()
val = val1 + val2
print val
endTime = time.time()
print endTime - startTime
很显然,这可以达到我们想要的效果,那么如果现在有更多的这样的函数需要我们去检查运行开销时间或者插入日志,我们有没有更加优雅的做法呢。
优雅的实现方式-AOP
是存在这样优雅的方式让我们在不改动原本代码结构的基本上插入新功能的,这种方式称为AOP~
什么是AOP
AOP(面向切面编程):AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
也就是说,通过面向切面编程的方式,我们能在保留原本代码的结构添加新的功能,降低代码之间的耦合度。
装饰器
装饰器是Python实现AOP的一个语法糖
,通过在方法或者类前面加@xxx
这样的形式来实现。如下形式:
@Log
def foo():
#...do sth
pass
通过以上的方式,我们可以对在方法执行前或者后进行我们想要的操作例如打印函数调用消耗的时间或者加入日志输出,插入的实现是在我们的Log方法中。
装饰器及其实现原理
在Python中,函数也是对象,所以可以将函数作为参数进行传递。
def Log(func):
def WrapFunc(*args, **kwargs):
retVal = func(*args, **kwargs)
print "call func"
return retVal
return WrapFunc
def Add():
print "Call Add Func"
Add = Log(Add)
Add()
>>>Call Add Func
>>>call func
我们把函数Add
作为一个对象传给Log
,使用一个函数WrapFunc
将其包装起来,然后定制我们想要的日志功能,然后把WrapFunc
返回,这样我们就得到了一个被我们包装(装饰)
过的函数,这就是装饰器的实现原理。然后如果通过Python
的语法糖我们则可以得到下面优雅的代码:
def Log(func):
def WrapFunc(*args, **kwargs):
retVal = func(*args, **kwargs)
print "call func"
return retVal
return WrapFunc
@Log
def Add():
print "Call Add Func"
Add()
上面优雅的代码就是装饰器的实现,我们没有修改Add
函数的实现,而实现了日志功能的输出,非常的Pythonic
,但是装饰器能做到的不止这些,我们还可以传入参数。
进阶的装饰器
接下来我们来实现一个可以传入参数的装饰器,用来实现打印我们想要的信息。
def LogWithStr(sOutput):
def Log(func):
def WrapFunc(*args, **kwargs):
retVal = func(*args, **kwargs)
print sOutput
return retVal
return WrapFunc
return Log
@LogWithStr("Hey Man")
def Add():
print "Call Add Func"
Add()
>>>Call Add Func
>>>Hey Man
上面这个装饰器具体步骤其实也很简单
Add = LogWithStr("HeyMan")(Add)
总结:
- 装饰器是AOP的一种实现,我们可以在不更改原本代码结构的基础上插入新功能
- 装饰器的实现原理其实是闭包,通过将函数作为参数传入闭包函数来实现
- 可以装饰器可以传入任何我们想要的参数来实现我们想要的效果