Python和C++有一些不同,我们现在要理解python的装饰器,首先要了解这些不同
一、python是一种弱类型语言,变量名称和内存中的对象只是一种映射关系,相同的变量名称可以在不同的时候映射到不同类型的对象去
二、函数在python中是一等公民,python里的函数享有和变量同等的地位,支持高阶函数(Higher-order function)
然后我们要明白装饰器是什么,装饰器是一种设计模式,经常被用于有切面需求(AOP)的场景,起作用是抽离出大量函数中,与函数本身公共无关的,可复用代码,进行复用。简单的说,装饰器就是为已经存在的完整的对象添加额外的功能。
废话结束,我们开始讲python的装饰器。
假设我们已经有了一个函数,如下:
现在我们突然有新的需求了,希望获得这个函数的运行时间。OK,很简单,我们修改函数如下:
这个函数看似实现了我们的需求,但这个函数有两个我们无法接受的严重问题:
1. 我们新添加的需求与函数原来的任务是没有什么关系的,这段代码本不应属于这个函数
2. 我们有可能还想获得其他函数的运行时间,而这段获得函数运行时间的代码可能被我们复制n份,代码冗余
说白了,我们希望把函数看成一个整体,我们希望这个函数是这段代码(获得函数运行时间的代码)的一部分,而我们不希望这段代码是函数的一部分
Python提供了这一种可能性,让我们想想函数是怎么产生的呢?我们发现我们希望对我们定义的变量进行一系列逻辑处理产生新的变量,这段处理是可复用的,可处理不同的变量,这时,我们编写了函数。
而现在,轮到函数了,我们希望对我们定义的函数进行一系列处理,这段处理是可复用的,可应用于不同的函数,这时,装饰器诞生了。
*由于函数在python中是一等公民 *
所以我们可以这么做:
这样看起来完美了,我们像对待变量一样对待了我们的函数,但是,新的问题产生了:我们改变了调用代码!如果我们在程序中调用了n次函数foo,难道我们现在要将他们都换成timeit(foo)
吗?不可接受吧
这是因为变量只是变量,而即使我们函数可以像变量一样传递,我们依然不希望放弃函数本身的作用,我们的目标是希望装饰器对我们的函数添砖加瓦,我们不希望调用装饰器本身,我们的期望是,给装饰器一个函数,装饰器为这个函数增加功能,然后再还给我们一个同样的函数!
于是我们继续修改代码如下:
这个其实没有什么难度了,既然函数可以作为参数,当然函数也可以作为返回值,我们在timeit函数内部构建了一个包裹传入函数的新的函数(这个过程中为传入函数添加功能),然后返回函数
于是,我们可以做如下理解,我们对函数的期望就是:输入是变量,输出也是变量,而我们对装饰器的期望则是:输入是函数,输出也是函数。当然,我们也可以输入函数返回变量或者反过来,我们永远没有强制要求,因为我们要理解,*我们现在在学习的,是一种思想,是一种设计,而不是一种语法,一种规定 *
如果我们要将函数看做变量,那函数也是一种特殊的变量,函数可以有参数,可以有返回值。我们思考上边的例子,如果foo函数有两个参数怎么办:
这段代码在编译的时候是不会有任何问题的,如果函数运行过程中没有调用过第13行代码中的foo函数的话,也不会有任何问题,但是一旦代码调用了经过timeit修饰过的foo,当程序执行到其中的第7行代码的时候,程序就会报错
怎么改呢?最简单的方法:
这段代码必然没问题,但很显然,这导致了timeit和foo函数的强耦合,这个计算函数运行时间的装饰器timeit只能装饰二参的函数,极端情况下只能装饰foo函数,那这和我们把代码直接写到foo函数当中又有什么区别呢?
Python提供了相关问题的解决方案,我们可以将装饰器的返回函数设置为可以接受任何参数并可以返回任何参数的情况,如下
函数foo不变,将包裹函数wrapper和调用函数func都改为了可以接受任何参数的形式。
此时,我们的计算函数时间的装饰器就可以针对任何函数了!
装饰器成为了一种函数的函数,Python为他提供了更简便的语法来应用,而不是傻傻的赋值与函数调用。其实现方法如下:
@timeit将其后面的函数执行如下操作
这样做的方法精简了代码、降低了字符输入量、并且可以在函数定义是一次调用,更简便也更容易理解
装饰器的语法允许我们在调用装饰器时提供其他参数,将timeit函数再次进行修改:
可以看出来,其实是对装饰器的有一层包装,这时会先执行add_info_timeit(‘hello world’)
,会返回一个装饰器,然后用这个装饰器去装饰函数foo
由于函数add_info_timeit
构成了一个带有环境参量的闭包,所以timeit和wrapper可以获得pre的值(获得可以,赋值不行,因为闭包只是捕获了非本地变量,但显然不会被允许写入任何被捕获的变量,详情查阅闭包相关知识)
关于这点,其实我的理解是,python的哲学将很多东西变得很灵活和优雅,同时也将很多权利和风险下放给了程序员。python取消了变量名称和类型之间的关联限制,同时打破了函数不像变量一样灵活的限制,这才使得python中实现装饰器这一设计模式变得很简单。但同时,更少的限制必然会导致更无下限的行为,就好比上边这个,嵌套了三层的函数,谁又说不能嵌套更多层呢?
http://www.cnblogs.com/vamei/archive/2013/02/16/2820212.html
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
http://doc.42qu.com/python/python-closures-and-decorators.html
http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
http://www.cnblogs.com/livingintruth/p/3305697.html
http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/