深入浅出python装饰器

python_decorator_logo
python_decorator_logo

python的装饰器是一种非常实用的功能。它可以在不改变现有函数的前提下增加该函数的功能。这样可以可以将大量重复的代码抽离出来提高代码复用率,在重构中让代码更加简洁清晰。尤其是在AOP的时候,这种优势就极为明显。说到这里熟悉Java的朋友多半会联想到诸如:javassist,CGLIB,AspectJ这些工具吧,没错与在Java中这些工具的功能类似,装饰器也具有同样的特性。在学习装饰器之前,各位看官姥爷应该对python闭包有一定的了解。如果不熟悉闭包,请参考我的前一篇文章《python闭包核心》,下面让我们先来看两个简单的例子,两个简单的函数:

输入结果为:

上面是一个简单的逻辑判断,当flag为1的时候调用good()函数,那么接下来我们稍微改造一下,在不修改good,bad函数本身的前提下,在执行两个函数前输出一段信息。

输出结果为:

此时调用方法已经改变,那么我们能否在不修改good,bad函数同时不修改good(),bad()调用方法的前提下,在执行两个函数前输出一段信息?

输出结果为:

上述代码输出结果与之前的一致,并且我们做到了没有修改good,bad函数的同时也没有修改good(),bad()调用方法。这就是装饰器的强大之处。在python中装饰器有特定的语法来实现(修改下装饰器语法):

上述代码与我们自己实现的方式是一致的,看起来是不是更简洁?作为一个曾经的Java爱好者忍不住想到了Tapestry4.1x和Tapestry5中的Annotations,没错,简直一模一样。接下来,如果我们的函数带有参数该如何去调用呢?

运行结果为:

参数的传递是不是很简单?但输出结果又有一点点意外?为何good函数中的输出没有被打印出来?看到上面注掉的的那一句了吗?那就是没有输出的原因。这里good经过装饰实际已经被替换成了decorator函数,带有输出语句的原始good函数则是作为参数f被传递到了showmore函数中。因此在decorator中通过调用f(name)实际就是调用了带有输出的原始good函数。(所以说装饰器实际就是闭包的一种高阶应用),为了证明这一点,我们改进一下:

起输出结果为:

通过debug可以看到f确实就是原始的good和bad函数。在执行到good函数的@showmore时候直接跳转到showmore(f)函数,此过程相当于good = showmore(good),这也与前文例子一样。因为闭包的特性,此时内部函数并没有执行,但print f(‘我是没有经过装饰的原始函数’)可以执行。那么为什么会是下面这种诡异的结果呢?

原因在于f(‘我是没有经过装饰的原始函数’)就是good(n)函数,在执行good(n)函数时可以打印出“good news 我是没有经过装饰的原始函数”,但因为good(n)函数没有返回值,所以此时print出来的就是None。那么问题来了,如果有返回值呢(让good函数有返回值)?

输出结果为:

与我们上面分析的一样,当good(n)函数有返回值,那么print的结果就不是None。同理,上面说过good(‘Mr.Good’)和bad(‘Mr.Bad’)实际上都是被装饰器装饰过的函数(就是替换成了decorator(name)函数),为了证明这点,我们来测试下,输出good(‘Mr.Good’),看看到底是什么:

输出结果为:

估计看到这个结果您已经不难才出原因了,这正是decorator(name)函数的输出结果,两个打印分别是自己的print和调用没被装饰过的good(n)函数打印出来的,至于None,因为decorator(name)函数本身没有返值所以就是None。那么如果我们让decorator(name)有返回值呢?让decorator(name)函数带有返回值,于是我们得到了一个带有返回值的装饰器:

输出结果为:

果然如我们所料想的,有了返回值。最后我们再看一个有趣的例子,如果多个装饰器作用在同一个函数上会是啥样子?

输出结果为:

About 歇歇脚|Java|Linux 1036 Articles
歇歇脚元老