python的装饰器是一种非常实用的功能。它可以在不改变现有函数的前提下增加该函数的功能。这样可以可以将大量重复的代码抽离出来提高代码复用率,在重构中让代码更加简洁清晰。尤其是在AOP的时候,这种优势就极为明显。说到这里熟悉Java的朋友多半会联想到诸如:javassist,CGLIB,AspectJ这些工具吧,没错与在Java中这些工具的功能类似,装饰器也具有同样的特性。在学习装饰器之前,各位看官姥爷应该对python闭包有一定的了解。如果不熟悉闭包,请参考我的前一篇文章《python闭包核心》,下面让我们先来看两个简单的例子,两个简单的函数:
# coding=utf-8 def good(): print 'good news' def bad(): print 'bad news' flag = 1 if flag: good() else: bad()
输入结果为:
good news
上面是一个简单的逻辑判断,当flag为1的时候调用good()函数,那么接下来我们稍微改造一下,在不修改good,bad函数本身的前提下,在执行两个函数前输出一段信息。
# coding=utf-8 def good(): print 'good news' def bad(): print 'bad news' def show(f): print 'welcome here~' f() flag = 1 if flag: show(good) else: show(bad)
输出结果为:
welcome here~ good news
此时调用方法已经改变,那么我们能否在不修改good,bad函数同时不修改good(),bad()调用方法的前提下,在执行两个函数前输出一段信息?
# coding=utf-8 def good(): print 'good news' def bad(): print 'bad news' def showmore(f): #这里是闭包同时也是装饰器 def decorator(): print 'welcome here~' f() return decorator good = showmore(good) bad = showmore(bad) flag = 1 if flag: good() else: bad()
输出结果为:
welcome here~ good news
上述代码输出结果与之前的一致,并且我们做到了没有修改good,bad函数的同时也没有修改good(),bad()调用方法。这就是装饰器的强大之处。在python中装饰器有特定的语法来实现(修改下装饰器语法):
# coding=utf-8 def showmore(f): def decorator(): print 'welcome here~' f() return decorator @showmore def good(): print 'good news' @showmore def bad(): print 'bad news' flag = 1 if flag: good() else: bad()
上述代码与我们自己实现的方式是一致的,看起来是不是更简洁?作为一个曾经的Java爱好者忍不住想到了Tapestry4.1x和Tapestry5中的Annotations,没错,简直一模一样。接下来,如果我们的函数带有参数该如何去调用呢?
# coding=utf-8 def showmore(f): def decorator(name): #参数在这里 print 'welcome here~'+name #f(name) #注意,这里被注释掉了 return decorator @showmore def good(n): print 'good news '+n @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: good('Mr.Good') else: bad('Mr.Bad')
运行结果为:
welcome here~Mr.Good
参数的传递是不是很简单?但输出结果又有一点点意外?为何good函数中的输出没有被打印出来?看到上面注掉的的那一句了吗?那就是没有输出的原因。这里good经过装饰实际已经被替换成了decorator函数,带有输出语句的原始good函数则是作为参数f被传递到了showmore函数中。因此在decorator中通过调用f(name)实际就是调用了带有输出的原始good函数。(所以说装饰器实际就是闭包的一种高阶应用),为了证明这一点,我们改进一下:
# coding=utf-8 def showmore(f): print f('我是没有经过装饰的原始函数') #这里调用传递过来的函数 def decorator(name): print 'welcome here~'+name f(name) return decorator @showmore def good(n): print 'good news '+n @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: good('Mr.Good') else: bad('Mr.Bad')
起输出结果为:
good news 我是没有经过装饰的原始函数 None bad news 我是没有经过装饰的原始函数 None welcome here~Mr.Good good news Mr.Good
通过debug可以看到f确实就是原始的good和bad函数。在执行到good函数的@showmore时候直接跳转到showmore(f)函数,此过程相当于good = showmore(good),这也与前文例子一样。因为闭包的特性,此时内部函数并没有执行,但print f(‘我是没有经过装饰的原始函数’)可以执行。那么为什么会是下面这种诡异的结果呢?
good news 我是没有经过装饰的原始函数 None
原因在于f(‘我是没有经过装饰的原始函数’)就是good(n)函数,在执行good(n)函数时可以打印出“good news 我是没有经过装饰的原始函数”,但因为good(n)函数没有返回值,所以此时print出来的就是None。那么问题来了,如果有返回值呢(让good函数有返回值)?
# coding=utf-8 def showmore(f): print f('我是没有经过装饰的原始函数') def decorator(name): print 'welcome here~'+name f(name) return decorator @showmore def good(n): print 'good news '+n return 'goodbye~'+n #这里增加了返回值 @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: good('Mr.Good') else: bad('Mr.Bad')
输出结果为:
good news 我是没有经过装饰的原始函数 goodbye~我是没有经过装饰的原始函数 bad news 我是没有经过装饰的原始函数 None welcome here~Mr.Good good news Mr.Good
与我们上面分析的一样,当good(n)函数有返回值,那么print的结果就不是None。同理,上面说过good(‘Mr.Good’)和bad(‘Mr.Bad’)实际上都是被装饰器装饰过的函数(就是替换成了decorator(name)函数),为了证明这点,我们来测试下,输出good(‘Mr.Good’),看看到底是什么:
# coding=utf-8 def showmore(f): def decorator(name): print 'welcome here~'+name f(name) return decorator @showmore def good(n): print 'good news '+n @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: print good('Mr.Good') else: print bad('Mr.Bad')
输出结果为:
welcome here~Mr.Good good news Mr.Good None
估计看到这个结果您已经不难才出原因了,这正是decorator(name)函数的输出结果,两个打印分别是自己的print和调用没被装饰过的good(n)函数打印出来的,至于None,因为decorator(name)函数本身没有返值所以就是None。那么如果我们让decorator(name)有返回值呢?让decorator(name)函数带有返回值,于是我们得到了一个带有返回值的装饰器:
# coding=utf-8 def showmore(f): def decorator(name): print 'welcome here~'+name f(name) return 'goodbye~~'+name #这里增加了返回值 return decorator @showmore def good(n): print 'good news '+n @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: print good('Mr.Good') else: print bad('Mr.Bad')
输出结果为:
welcome here~Mr.Good good news Mr.Good goodbye~~Mr.Good
果然如我们所料想的,有了返回值。最后我们再看一个有趣的例子,如果多个装饰器作用在同一个函数上会是啥样子?
# coding=utf-8 def showmore(f): #装饰器1 def decorator(name): print 'welcome here~'+name f(name) return 'goodbye~~'+name return decorator def printPre(f): #装饰器2 def showPre(name): print "$"*20 f(name) return '$~~' + name return showPre def printSufx(f): #装饰器3 def showSufx(name): print "*"*20 f(name) return '*~~' + name return showSufx @printPre @showmore @printSufx def good(n): print 'good news '+n @showmore def bad(n): print 'bad news '+ n flag = 1 if flag: print good('Mr.Good') else: print bad('Mr.Bad')
输出结果为:
$$$$$$$$$$$$$$$$$$$$ welcome here~Mr.Good ******************** good news Mr.Good $~~Mr.Good