python装饰器

简介

python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

简单的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time

def count_time(func):
def wrapper():
t1 = time.time()
func()
print("执行时间为:", time.time() - t1)
return wrapper

@count_time # 语法糖
def baiyu():
print("我是攻城狮白玉")
time.sleep(2)

if __name__ == '__main__':
# baiyu = count_time(baiyu) # 因为装饰器 count_time(baiyu) 返回的是函数对象 wrapper,这条语句相当于 baiyu = wrapper
# baiyu() # 执行baiyu()就相当于执行wrapper()
baiyu() # 用语法糖之后,就可以直接调用该函数了

装饰器传参

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

def count_time(func):
# 此处,我们的wrapper函数的参数为*args和**kwargs,表示可以接受任意参数
# 这时我们就可以调用我们的装饰器了。
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
return wrapper

@count_time
def blog(name):
print('进入blog函数')
name()
print('我的博客')

if __name__ == '__main__':
# baiyu = count_time(baiyu) # 因为装饰器 count_time(baiyu) 返回的时函数对象 wrapper,这条语句相当于 baiyu = wrapper
# baiyu() # 执行baiyu()就相当于执行wrapper()
# baiyu() # 用语法糖之后,就可以直接调用该函数了
blog(baiyu)
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
26
27
28
29
#带有不定参数的装饰器
import time

def deco(func):
def wrapper(*args, **kwargs):
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper

@deco
def func(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))

@deco
def func2(a,b,c):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b+c))

if __name__ == '__main__':
f = func
func2(3,4,5)
f(3,4)
#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
26
27
28
29
30
31
32
import time

def count_time_args(msg=None):
def count_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print(f"[{msg}]执行时间为:", time.time() - t1)

return wrapper
return count_time

@count_time_args(msg="baiyu")
def fun_one():
time.sleep(1)

@count_time_args(msg="zhh")
def fun_two():
time.sleep(1)

@count_time_args(msg="mylove")
def fun_three():
time.sleep(1)

if __name__ == '__main__':
fun_one()
fun_two()
fun_three()

# [baiyu]执行时间为:
# [zhh]执行时间为:
# [mylove]执行时间为:

类装饰器

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import time

class BaiyuDecorator:
def __init__(self, func):
self.func = func
print("执行类的__init__方法")

def __call__(self, *args, **kwargs):
print('进入__call__函数')
t1 = time.time()
self.func(*args, **kwargs)
print("执行时间为:", time.time() - t1)

@BaiyuDecorator
def baiyu():
print("进入baiyu函数")
time.sleep(2)

def python_blog_list():
time.sleep(5)
print('1111')

@BaiyuDecorator
def blog(name):
print('进入blog函数')
name()
print('2222')

if __name__ == '__main__':
baiyu()
print('--------------')
blog(python_blog_list)

# 执行类的__init__方法
# 执行类的__init__方法
# 进入__call__函数
# 进入baiyu函数
# 执行时间为: 2.004128932952881
# --------------
# 进入__call__函数
# 进入blog函数
# 1111
# 2222
# 执行时间为: 5.0075788497924805

带参数的类装饰器

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
26
27
28
29
30
31
32
33
34
35
36
37
# 当装饰器有参数的时候,__init__() 函数就不能传入func(func代表要装饰的函数)了
# 而func是在__call__函数调用的时候传入的。

class BaiyuDecorator:
def __init__(self, arg1, arg2): # init()方法里面的参数都是装饰器的参数
print('执行类Decorator的__init__()方法')
self.arg1 = arg1
self.arg2 = arg2

def __call__(self, func): # 因为装饰器带了参数,所以接收传入函数变量的位置是这里
print('执行类Decorator的__call__()方法')
def baiyu_warp(*args): # 这里装饰器的函数名字可以随便命名,只要跟return的函数名相同即可
print('执行wrap()')
print('装饰器参数:', self.arg1, self.arg2)
print('执行' + func.__name__ + '()')
func(*args)
print(func.__name__ + '()执行完毕')
return baiyu_warp

@BaiyuDecorator('Hello', 'Baiyu')
def example(a1, a2, a3):
print('传入example()的参数:', a1, a2, a3)

if __name__ == '__main__':
print('准备调用example()')
example('Baiyu', 'Happy', 'Coder')
print('测试代码执行完毕')

# 执行类Decorator的__init__()方法
# 执行类Decorator的__call__()方法
# 准备调用example()
# 执行wrap()
# 装饰器参数: Hello Baiyu
# 执行example()
# 传入example()的参数: Baiyu Happy Coder
# example()执行完毕
# 测试代码执行完毕

装饰器的顺序

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
26
27
28
29
30
31
32
33
34
def dec1(func):  
print("1111")
def one():
print("2222")
func()
print("3333")
return one

def dec2(func):
print("aaaa")
def two():
print("bbbb")
func()
print("cccc")
return two

@dec1
@dec2
def test():
print("test test")

test()

# aaaa
# 1111
# 2222
# bbbb
# test test
# cccc
# 3333

# 装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。
''' 17~20行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one,然后进行赋值。 '''
''' 用函数替代了函数名test。 22行则是实际调用被装载的函数,这时实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。 '''