wd and cc

-- Good good study, day day up!

python 的 decorator 学习

#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

  1. 被装饰的函数 add
  2. mydeco 装饰方法
  3. 装饰方法返回的方法

装饰器可以做的事情:

  1. timing,统计函数执行的时间。
  2. 控制函数 60 秒执行一次。通过在 dec 方法内记录一个最后执行时间实现。
  3. 类似上面的,可以任意指定限制的时间。只需要在上面的函数上面再套一层就可以。
  4. 实现结果记忆。同样的输入,总是同样的输出,那可以按照输入参数记录执行结果,下次相同参数就不用再次执行了。
  5. 给对象增加属性。可以装饰某给对象,然后增加一些属性,比如定制一下 __repr__ 输出更好的内容。
  6. 类似的,可以装饰某个对象,然后给他增加一些属性,比如增加一个 __create_at 这样的属性记录一下实例创建时间。
comments powered by Disqus