什么是装饰器

装饰器就是一个函数,而这个函数用来给其他函数添加额外的功能

为何需要用装饰器

  • 能够在不改变原函数功能的基础上,添加其他功能
  • 降低代码的冗余
  • 无需修改原函数的调用方式

如何使用

无参装饰器的实现方法

为下面的代码添加计算运算执行时间的功能

1
2
3
4
5
import time
def index():
print('start')
time.sleep(3)
print('end')

实现方法1:

1
2
3
4
5
6
7
8
9
10
import time
def index():
start=time.time()
print('start')
time.sleep(3)
print('end')
end=time.time()
print('执行时间为:%s'%(end-start))
# 调用
index()

这样不仅修改了原函数,且若其他函数也需要计算运行时间,也必须在函数体内部加这些代码,使得代码冗余

实现方法2:

1
2
3
4
5
6
7
8
9
import time
def index():
print('start')
time.sleep(3)
print('end')
start = time.time()
index()
end = time.time()
print('执行时间为:%s' % (end - start))

这样虽然没有修改原函数,但是若其他函数也需要计算运行时间,也需要在调用前后获取当前时间,使得代码冗余

实现方法3:

1
2
3
4
5
6
7
8
9
10
11
12
import time
def index():
print('start')
time.sleep(3)
print('end')

def count_time(func):
start=time.time()
func()
end=time.time()
print('执行时间为%s'%(end-start))
count_time(index)

这样既没有修改原函数代码,也能够解决多个函数都需计算运行时间时会出现代码冗余的问题,但是该方法使得原函数的调用方式改变了

实现方法4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import time
def index():
print('start')
time.sleep(3)
print('end')

def count_time(func):
def wrapper():
start=time.time()
func()
end=time.time()
print('执行时间为%s'%(end-start))
return wrapper

index=count_time(index)
index()

这样解决了原函数调用方式被改变的问题,只不过实现需要进行一步index=count_time(index)操作

实现方法5:

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


def count_time(func):
def wrapper():
start = time.time()
func()
end = time.time()
print('执行时间为%s' % (end - start))

return wrapper


@count_time
def index():
print('start')
time.sleep(3)
print('end')


index()

在python中有这样一个语法,在原函数前面加上[@函数名],这个的意义在于执行了index=count_time(index)操作,因此再调用的时候直接执行index()即调用了该函数

有参装饰器的实现方法

实现方法1:

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


def count_time(func):
def wrapper(x,y):
start = time.time()
func(x,y)
end = time.time()
print('执行时间为%s' % (end - start))

return wrapper


@count_time
def index(x,y):
print(x)
time.sleep(3)
print(y)


index(10,20)

该方法中,将原函数的参数传递给wrapper函数;但是若是其他函数被装饰,例如另外一个函数happy(x),内部只有一个参数,则上述代码存在局限性

实现方法2:

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


def count_time(func):
def wrapper(*args,**kwargs):
start = time.time()
func(*args,**kwargs)
end = time.time()
print('执行时间为%s' % (end - start))

return wrapper


@count_time
def index(x,y):
print(x)
time.sleep(3)
print(y)


index(10,20)

args和*kwargs 这两个是python中方法的可变参数,因此用这样的传参方式解决了被装饰函数即原函数的参数不统一问题

原函数带有返回值的装饰器实现方法

在上文中我们使用的index函数并没有返回值,若index函数有返回值该如何处理,实现方法如下

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


def count_time(func):
def wrapper(*args,**kwargs):
start = time.time()
res=func(*args,**kwargs)
end = time.time()
print('执行时间为%s' % (end - start))
return res

return wrapper


@count_time
def index(x,y):
print(x)
time.sleep(3)
print(y)
return 'hello'


a=index(10,20)

装饰器模板

1
2
3
4
5
6
7
def outter(func):
def wrapper(*args, **kwargs):
# 新功能代码
res = func(*args, **kwargs)
return res

return wrapper

例子:云笔记系统需登录才可获取笔记列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 登录验证
def checklogin(func):
def wrap(request, *args, **kwargs):
if 'username' not in request.session or 'id' not in request.session:
return HttpResponse('请登录后操作')
return func(request, *args, **kwargs)

return wrap


@checklogin
def index(request):
# 获取笔记列表
id = request.session['id']
notes = Note.objects.filter(user_id=id)
return render(request, 'note/index.html', locals())