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