所谓装饰器,就是一个函数,只不过它的操作对象是其他函数,是为帮某些函数添加功能而存在的,但在添加功能的时候有两个限制要求:
- 不能改变原函数的源码
- 不能改变原函数的调用方式
首先,在理解装饰器之前,我们需要了解一些概念
变量定义
与很多语言不同,python在定义变量时,不需要进行声明,直接赋值即可,如
a = 2
b = 2
print a,b
2 2
b = 3
print a,b
2 3
在进行“a = 2”的时候,相当于在内存中创建了一个“2”的对象,然后把对象“2”的地址赋给了 a ,如果这个时候,我们继续“b = 2“,python不会继续创建一个”2“对象,而是直接将”2“的地址赋给了 b ,且“2”这个对象的计数器加 1 (这牵涉到python内存管理的一些概念,就不加赘述),如果再继续”b = 3”则是又创建了一个“3”的对象,然后将地址赋给 b ,对 a 并没有改变。这样说有点乱,大家看图
函数的定义
python中函数的定义利用关键字“def”:
def function():
print "this is an function"
function()
this is an function
是不是感觉函数的定义和变量定义十分相似,没错,其实定义一个函数与定义一个变量并没有什么很大的差别,函数名就相当于我们的变量名。大家都知道,变量可以作为参数传给函数,那么函数名是否也能作为参数传给其他函数呢?别着急,继续往下看。
函数名作为参数
def function1():
print "this is an function1"
def function2(fuc):
fuc()
print "this is an function2"
function2(function1)
#运行结果
this is an function1
this is an function2
看到了没有,函数名是可以作为参数传递的,在上面的代码中,我们将“function1”作为参数传给了“function2”,在“function2”中,通过函数名调用“function1”,同样可以实现了“function1”的功能。
而对于“function2”我们是不是既实现了“function1”的功能,还实现了一些“function1”不具备的功能,是不是在没有修改“function1”源代码的基础上增加了某些功能,有没有,有没有,就说有没有!!!
那么这个是不是就是装饰器了,恭喜你,答错了!有很多眼尖的朋友肯定已经发现了,在开篇的两个要求中,只满足了“不修改函数源代码”这个一个要求;函数的调用方式已经改变了,之前是“function1()”,现在变成了“function2()”。这个要如何实现呢,后面就让我们来揭开装饰器的庐山真面目!!!
简单的decorator(装饰器)
def function1():
print "this is an function1"
def decorat(func):
def function2():
func()
print "this is an function2"
return function2
function1 = decorat(function1)
function1()
#运行结果
this is an function1
this is an function2
在这段代码中,我们都做了些什么惊天动地的大事呢?一起来看看
- 首先我们定义了一个简单函数“function1”
- 然后又定义了一个“decorat”函数,这个函数带了一个参数“func”我们在“decorat”函数中又嵌套了一个“function2”函数,在“function2”的函数体中,我们调用了“func()”到这里,大家应该不难看出,之前传进来的参数“func”应该是一个函数名,调用结束后面,我们还添加一点其他的代码(你希望增加的其他功能),定义完“function2”,我们将“function2”这个函数名作为“decorat”的结果返回出去
- 紧接着,我们通过“function1 = decorat(function1)”这条语句调用了“decorat”函数,是不是相当于将“function1”作为“func”传给了函数“decorat”,然后再将“decorat”的返回值赋给了“function1”
- 最后调用“function1()”,这个时候,“function1”还是我们刚开始定义的那个“function1”吗?答案肯定不是,这个时候的“function1”已经被返回的“function2”替换,我们调用“function1()”的时候,就相当于调用了“function2()”
- 就这样,我们在既没有修改原函数源代码以及调用方式的前提下,悄咪咪的增加了一些我们想给它添加的功能。是不是很神奇。
但按这种操作,每装饰一个函数,都需要“function1 = decorat(function1)”这样的一条语句,是不是很麻烦,不符合python简洁的风格,所以python提供了一个非常简便的方法:@decorat
def decorat(func):
def function2():
func()
print "this function was decorated"
return function2
@decorat
def function1():
print "this is an function1"
@decorat
def function2():
print "this is an function2"
function1()
function2()
#运行结果
this is an function1
this function was decorated
this is an function2
this function was decorated
只需要在每个你需要装饰的函数上面紧挨着加上一条“@decorat”语句,这个也叫语法糖,就像糖一样“粘”在函数的前面,这里的“decorat”是你自己的装饰器。也就是说“@decorat”等同于“function = decorat(function)”
被装饰的函数带参数
前面的被装饰函数都是不带参数的,属于最简单的形式,接下来,我们来看一下,如何装饰带参数的函数
def decorat(func):
def function2(name):
func(name)
print "this function was decorated"
return function2
@decorat
def function1(name):
print "this is an function1"
print "my name is "+name
@decorat
def function2(name):
print "this is an function2"
print "my name is " + name
function1("Tom")
function2("Alice")
#运行结果
this is an function1
my name is Tom
this function was decorated
this is an function2
my name is Alice
this function was decorated
在这个例子里面,大家不难看出,我给被装饰函数增加了一个“str”类型的参数,而它也成功被装饰了,这是怎么实现的呢?其实不难,细心的小伙伴可能已经发现了,只要我们在装饰器中内嵌的那个函数也含有同样的形参即可。但这样也造成了一个麻烦,无法实现无参函数的装饰,这里教大家一个小技巧,可变参数
def decorat(func):
def function2(*args,**kwargs):
func(*args,**kwargs)
print "this function was decorated"
return function2
@decorat
def function1(name):
print "this is an function1"
print "my name is "+name
@decorat
def function2():
print "this is an function2"
print "this function do not have name"
function1("Tom")
function2()
#运行结果
this is an function1
my name is Tom
this function was decorated
this is an function2
this function do not have name
this function was decorated
带参数的装饰器
前面说了一下如何装饰带参数的函数,那么可能有人会问,装饰器能不能也带参数呢?对某些函数区别的进行装饰,一个装饰器实现多个功能?答案是肯定的。话不多说,直接上例子,没有什么比代码更实在的。
import time
def decorat(mult):
def wrapp(func):
def function2(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
wast_time = end_time - start_time
print mult,"倍的 wast_time =",wast_time*mult
print "this function was decorated-------------------\n"
return function2
return wrapp
#如果我想实现计算function1的运行时间并打印
#以及打印function2的运行时间的两倍
@decorat(1)
def function1(name):
print "this is an function1"
time.sleep(3)
print "my name is "+name
@decorat(2)
def function2():
print "this is an function2"
time.sleep(3)
print "this function do not have name"
function1("Tom")
function2()
#运行结果
this is an function1
my name is Tom
1 倍的 wast_time = 3.0150001049
this function was decorated-------------------
this is an function2
this function do not have name
2 倍的 wast_time = 6.04199981689
this function was decorated-------------------
通过这个例子,大家可以看出,装饰器是可以带参数的;但带参数的装饰器需要多嵌套 1 重函数,用来接收你调用装饰器的时候传进来的参数,这段代码中的“wrapp”其实就相当于前面几次代码中的“decorat”,用来接收你的被装饰函数名,而这里的“decorat”则是用来接收你调用装饰器时传进来的参数。
装饰器的调用顺序
装饰器的调用顺序是和语法糖的书写顺序相反的
import time
def decorat2(func):
def wrapp():
func()
print "this is decorat2"
return wrapp
def decorat(mult):
def wrapp(func):
def function2(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
wast_time = end_time - start_time
print mult,"倍的 wast_time =",wast_time*mult
print "this function was decorated-------------------\n"
return function2
return wrapp
@decorat2
@decorat(2)
def function2():
print "this is an function2"
time.sleep(3)
print "this function do not have name"
function2()
#运行结果
this is an function2
this function do not have name
2 倍的 wast_time = 6.00600004196
this function was decorated-------------------
this is decorat2