python 的 decorator 学习
最近学习了一下 python 的 decorator(装饰器),看的是这篇,Python修饰器的函数式编程, 觉得挺有意思的,写点东西记录一下。
装饰器简单讲就是返回一个函数的函数/类。看个简单的例子。
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4
5def dec1(fn):
6 print('inside dec1')
7
8 def wrapper():
9 print('inside wrapper')
10 return fn()
11 return wrapper
12
13
14@dec1
15def f1():
16 print('inside f1')
17
18if __name__ == '__main__':
19 print('begin exec')
20 f1()
21 print('end exec')
22
23# 执行结果:
24# inside dec1
25# begin exec
26# inside wrapper
27# inside f1
28# end exec
看上面例子能看到,装饰器生效有 2 个步骤,第一个是装饰,第二个是执行。上面装饰器的效果,和下面的代码的效果是一样。
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4
5def dec1(fn):
6 print('inside dec1')
7
8 def wrapper():
9 print('inside wrapper')
10 return fn()
11 return wrapper
12
13
14# @dec1
15def f1():
16 print('inside f1')
17
18if __name__ == '__main__':
19 print('begin exec')
20 dec1(f1)()
21 print('end exec')
22
23# 执行结果:
24# begin exec
25# inside dec1
26# inside wrapper
27# inside f1
28# end exec
可以看到除了 「begin/end exec」,其他部分执行结果是一样的。所以理解装饰器,就把 @dec1
换成 dec1(fn)()
这么理解就可以了。
有时候会看到类也可以作为装饰器使用。其实理解起来也类似。举个例子。
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4
5class dec1(object):
6 def __init__(self, fn):
7 print('inside dec1')
8 self.fn = fn
9
10 def __call__(self):
11 print('inside wrapper')
12 return self.fn()
13
14
15@dec1
16def f1():
17 print('inside f1')
18
19if __name__ == '__main__':
20 print('begin exec')
21 f1()
22 print('end exec')
23
24# 执行结果:
25# inside dec1
26# begin exec
27# inside wrapper
28# inside f1
29# end exec
这里和上面类似,把 @dec1
理解成 dec1(fn)()
,不过是这里的 dec1
是个类,那么 dec1(fn)
其实是调用的 dec1.__init__(fn)
,那么后续的 dec1(fn)()
就是调用产生的对象的 dec1.__call__()
了。
有时候还能看到加了参数的装饰器。加了参数的是怎么回事呢。再看下面的例子。
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4
5def dec1(name):
6 print('inside dec1')
7
8 def real_dec1(fn):
9 def wrapper():
10 print('inside wrapper')
11 return fn()
12 return wrapper
13 return real_dec1
14
15
16@dec1(name='1')
17def f1():
18 print('inside f1')
19
20if __name__ == '__main__':
21 print('begin exec')
22 f1()
23 print('end exec')
24
25# 执行结果:
26# inside dec1
27# begin exec
28# inside wrapper
29# inside f1
30# end exec
看懂了没有,就是多了个嵌套而已。遇到加了参数的,那就是把之前的没有参数的部分返回回来就可以了。等价的例子就不贴了,这个等价于 dec1(name='1')(fn)()
。
如果是类装饰器,并且有参数,那等价于 dec1(name='1')(fn)()
,其中 __init__(self, name)
先处理第一层参数,然后 __call__(fn)
处理第二层,然后需要在 __call__
里面再定义一个 wrapper 返回。
说明白没有?呵呵。
补充一点新的知识,来自于这个视频。
1@mydeco
2def add(a, b):
3 return a + b
上面这个等价于下面这个
1def add(a, b):
2 return a + b
3
4add = mydeco(add)
这里面三个 callable
- 被装饰的函数 add
- mydeco 装饰方法
- 装饰方法返回的方法
装饰器可以做的事情:
- timing,统计函数执行的时间。
- 控制函数 60 秒执行一次。通过在 dec 方法内记录一个最后执行时间实现。
- 类似上面的,可以任意指定限制的时间。只需要在上面的函数上面再套一层就可以。
- 实现结果记忆。同样的输入,总是同样的输出,那可以按照输入参数记录执行结果,下次相同参数就不用再次执行了。
- 给对象增加属性。可以装饰某给对象,然后增加一些属性,比如定制一下
__repr__
输出更好的内容。 - 类似的,可以装饰某个对象,然后给他增加一些属性,比如增加一个
__create_at
这样的属性记录一下实例创建时间。