装饰器

之前对于装饰器的理解感觉比较片面,没成体系,在此记录一下装饰器的学习之路

Decorators is to modify the behavior of the function through a wrapper so we don’t have to actually modify the function.

也就是说,所谓装饰器,就是通过装饰器函数,来修改原函数的一些功能,使原函数在不需要修改的情况下达到某些目的。通常广泛应用于日志,身份认证,缓存等方面。

函数装饰器

先看一个简单例子

1
2
3
4
5
6
7
8
9
10
11
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper

@my_decorator
def greet():
print('hello world')

greet()

@被称为语法糖,@my_decorator就相当于my_decorator(greet),当然,my_decorator和greet函数也能带参数

1
2
3
4
5
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper

这样也不是万无一失的,我们发现greet函数的元信息发生了变化

1
2
3
4
5
6
7
8
9
greet.__name__
## 输出
'wrapper'

help(greet)
# 输出
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

为了解决这个问题,通常使用内置装饰器@functools.wrap,它会帮助我们保留函数的元信息

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

def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper

@my_decorator
def greet(message):
print(message)

greet.__name__

# 输出
'greet'

类装饰器

不仅不可以使用函数装饰器,类也可以使用装饰器。类装饰器主要依赖于__call__函数

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
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0

def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)

@Count
def example():
print("hello world")

example()

# 输出
num of calls is: 1
hello world

example()

# 输出
num of calls is: 2
hello world

...

装饰器也可以是嵌套使用

1
2
3
4
5
@decorator1
@decorator2
@decorator3
def func():
...

相当于

1
decorator1(decorator2(decorator3(func)))

实际用法

最常用的应该还是日志记录。如果想要测试某些函数的耗时时长,装饰器就是一种常用方式

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

def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
return wrapper

@log_execution_time
def calculate_similarity(items):
...

对于身份认证来说,往往使用需要登录之后才能使用的功能,例如评论,在校验这种身份的时候也经常使用装饰器来校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import functools

def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数 post_comment()
else:
raise Exception('Authentication failed')
return wrapper

@authenticate
def post_comment(request, ...)
...

另外,对于测试来说,往往需要传入某种格式固定的参数,例如传入值的先后顺序时候合法等,也常用装饰器来进行校验

1
2
3
4
5
6
7
8
9
10
import functools

def validation_check(input):
@functools.wraps(func)
def wrapper(*args, **kwargs):
... # 检查输入是否合法

@validation_check
def neural_network_training(param1, param2, ...):
...
-------------本文结束感谢您的阅读-------------