装饰器是python里面比较重要的内容之一,就我个人学习装饰器的历程而言,真是装饰器虐我千百遍,我待装饰器如初恋。。。这东西来来回回学了好多遍,当时是搞明白了,可是工作不会用到,过段时间,回过头来想想装饰器是啥?它的原理是什么?我就又不记得了,所以还是记下来比较好吧!

装饰器介绍

装饰器是一种用来装饰函数的函数,它有两个原则:1.在不改变原函数的代码的情况下装饰函数 2.在不改变原函数调用方式的情况下装饰函数

函数及变量

在python中,函数名也是一个变量,可以有赋值等操作。其实在c语言中,函数名的本质也就是一个指向此函数名对应的函数的代码段,有c语言基础可能理解这一点会比较容易
代码示例如下:

1
2
3
4
5
def func():
print("this is a func")

a = func
a()

这段代码运行结果就是”this is a func”,给a赋值func后,a也就是这个函数类型,当然赋值时候不可以在后面加括号,加了括号就是调用函数了,会把func的返回值赋值给a,没有的话a就等于None,在c语言中这么做的话要用函数指针,这一点来说python方便太多了

函数内定义函数

有了上面的基础,那么再来看看在函数体内定义函数的情况是咋样的

1
2
3
4
5
6
7
def outer_func():
print("before inner_func")
def inner_func():
print("i am inner_func")
print("after inner_func")

outer_func()

运行结果:

1
2
before inner_func
after inner_func

从运行结果来看,里边定义的函数并没有被调用,也仅仅只是定义的情况,当然我们可以在内部进行调用,对上面代码稍作修改

1
2
3
4
5
6
7
8
def outer_func():
print("before inner_func")
def inner_func():
print("i am inner_func")
print("after inner_func")
inner_func()

outer_func()

运行结果:

1
2
3
before inner_func
after inner_func
i am inner_func

说明在内部调用了inner_func函数

装饰器雏形

有了上面两个小知识点的基础,那么就可以来讲一下装饰器了,先自己写一个装饰器原理分解代码,用来统计函数运行时间的装饰函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time

def timer(func):
print(func)
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("run time is %s"%(end_time-start_time))
return wrapper

def test1():
time.sleep(2)
print("this is test1")


def test2():
time.sleep(4)
print("this is test2")

test1 = timer(test1)
test2 = timer(test2)

test1()
test2()

运行结果如下:

1
2
3
4
5
6
<function test1 at 0x7f208c7ad510>
<function test2 at 0x7f208c7ada60>
this is test1
run time is 2.002680540084839
this is test2
run time is 4.004958152770996

解释:
上面的代码,前部分都是函数定义部分,没有调用,真正看到程序运行流程的是从test1 = timer(test1),这里开始往后,那么我就从这行代码开始分析。
这行代码就是把test1当做参数传给timer函数调用timer,然后把返回结果赋值给test1。
那么调用timer的流程就是先打印传递进去的参数,也就是test1,然后定义了一个内部的wrapper函数,wrapper在内部只是定义,并不会被调用。接下来返回wrapper,此时test1就变成了wrapper,test2过程也和上面一样。
接下来调用test1,就变成了调用内部的wrapper,那么结果不就显而易见了。

装饰器之语法糖

上面就是装饰器的原理分解,当然python语言提供了一个简便的方法,让我们来实现装饰器,也就是装饰器语法糖,在定义函数之前 @函数名 ,就可以使用装饰器了,把上面代码稍作修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import time

def timer(func):
print(func)
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("run time is %s"%(end_time-start_time))
return wrapper

@timer
def test1():
time.sleep(2)
print("this is test1")

@timer
def test2():
time.sleep(4)
print("this is test2")


test1()
test2()

运行一下,发现逻辑和上面完全一致(统计时间会有所变化)。也就是这种语法糖的效果就相当于我们写的test1 = timer(test1)

好了,装饰器的介绍先告一段落,之后再讲它的一些进阶用法。