前言

之前写过一篇博文:《Python的闭包与装饰器》,但是这篇博文重点讲的是闭包,对装饰器的讲解很少,最近对装饰有了一些新的认识,所以再次发文补充!

Python中的装饰器可以分为:函数装饰器和类装饰器

函数装饰器

首先提一下函数有关的几个前提知识:

  1. 函数也是对象,函数可以被赋值给一个变量。

  2. 函数可以作为参数,传给另一个函数。

  3. 在Python中,我们可以在函数中定义函数。

  4. 函数的返回值也可以是函数对象(闭包)。

有了上面的知识就不难理解装饰器了,下面进入正题!

函数装饰器的实质就是:装饰器是一个函数,它至少有一个函数作为参数,它返回值是一个函数对象!

不带参数的装饰器

直接看例子代码:

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

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

greet = my_decorator(greet)
greet()

# 输出
wrapper of decorator
hello world

带有参数的装饰器

如果被装饰的函数有参数,那么装饰器中可以直接加上对应的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def my_decorator(func):
def wrapper(message):
print('wrapper of decorator')
func(message)
return wrapper

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


greet('hello world')

# 输出
wrapper of decorator
hello world

如果另一个函数也想用这个装饰器,但是另一个函数的参数不一样了该怎么办?加上*args和**kwargs

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

带有自定义参数的装饰器

上面提到的参数都是被装饰函数所需要的参数,但是装饰器其实可以更灵活,它还可以接受自己定义的参数。

看例子:统计函数执行次数的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
return my_decorator

@repeat(4)
def greet(message):
print(message)

greet('hello world')

# 输出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world

类装饰器

类也可以作为装饰器,类装饰器主要依赖于函数__call__(),每当你调用一个类的示例时,函数__call__()就会被执行一次!

看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
...

总结

所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

装饰器应用广泛,最常见的就是身份认证、日志统计、输入合理性检查及缓存等。