几个概念
装饰器的用处是不需要大批量修改某函数就能实现为其增添功能。装饰器满足两点要求:
- 不能修改被装饰函数的源代码
- 不能修改被装饰函数的调用方式
在讲解装饰器之前,我们还需要明白几个表达方式。对一个函数test1()来说:
- test1表示的是函数的内存地址
- test1()就是在调用在test1这个地址的内容,也就是在调用函数
不带参数装饰器
这里我们先假设我们有一个函数test,我们想在不改变函数代码和调用方式的前提下,实现对test的运行时间计算。
不使用装饰器的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#python3
import time
def timer(func):
def running_time():
print('%s loaded'%func)
start = time.time()
res = func()#4
end = time.time()
print('func running time is %s'%(-start+end))
return res #6
return running_time #2
def test():#5
time.sleep(2)
print('test is running')
return 'test finished'
#调用方式
test = timer(test)#1
test()#3
上面的代码在最后两行调用的时候,我们先是把test作为内存地址传入timer,然后timer会执行#1,返回它的内嵌函数running_time的内存地址(注意不是调用),也就是说现在的test存的不再是test函数的内存地址,
而是running_time的内存地址,所以我们在#3调用test()时,其实执行的是running_time,然后在running_time内部再调用test(),计算它的运行时间,大致的流程可以参考注释中的顺序。
它的输出如下:
<function test at 0x00C21A08> loaded
test is running
func running time is 2.000366687774658
test finished
下面我们用@装饰器的语法来简化上述过程。只需要在被调用函数的前面加上@装饰器就可以了。最后函数的调用语句并没有改变(仍为test()),这也完成了某种程度上对该函数的改写。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#python3
import time
def timer(func):
def running_time(*args, **kwargs): #1
print('%s loaded'%func)
start = time.time()
res = func(*args, **kwargs) #2
end = time.time()
print('func running time is %s'%(-start+end))
return res
return running_time
def test(parameters=None):
time.sleep(2)
print('test is running')
return 'test finished'
test()
值得注意的是,被装饰的函数有时可能也有入参,那么我们可以在装饰器的内嵌函数的入参位置加上args和kwargs确保我们接受了全部的参数(见#1,#2),然后再传递给内嵌函数内对被装饰函数的调用处。
这段代码的输出和上一段一样。
带参数装饰器
当我们有多个被装饰的函数时,我们可能需要在装饰器中将他们区分开来进行处理,这就需要@装饰器(parameter=value)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#python3
import time
def timer(parameter): #3
def inside_timer(func): #5
def running_time(): #7
print('%s loaded'%parameter)
start = time.time()
res = func() #8
end = time.time()
print('func running time is %s'%(-start+end))
return res #11
return running_time #6
return inside_timer #4
def test(): #8
time.sleep(2)
print('test is running') #9
return 'test finished' #10
test() #1
输出是
task1 loaded
test is running
func running time is 2.000399112701416
test finished
如果我们不用装饰器这个语法,那么最后的调用过程为:
timer=timer(parameter=value) #执行完毕后,timer存储的是inside_timer的内存地址
test=timer(test) #将test作为入参传入inside_timer,返回running_time的内存地址给test
test() #这一步的执行过程和无参数的装饰器都一样
参考来源
如果看完还有疑惑可以参考:http://lib.csdn.net/article/python/62942