深入理解Python 装饰器(decorator)
2018-02-27 14:53
656 查看
刚看到Python装饰器时, 觉得很神奇。简单实验下,发现也就那么回事。但是慢慢的看到越来越多的装饰器。很多时候又不了解到底是怎么回事了。
最后还是决定好好研究下。
先看看一些实例, 然后再来分析下原理
假设我们有如下的基本函数
如果还是不清楚,那就继续看下面的内容。
1. 装饰器函数运行在函数定义的时候
2. 装饰器需要返回一个可执行的对象
3. 装饰器返回的可执行对象要兼容函数f的参数
但事实是, 这其实还不够
前面我们讲的都是函数式的装饰器, 那么类能不能成为装饰器呢?
在Python中, 其实函数也是对象。 反过来, 对象其实也可以像函数一样调用, 只要在类的方法中实现__call__方法。回想一下创建对象的过程
带参数的装饰器
fun = A.__init__(args)(fun)
不带参数的装饰器
fun = A.__init__(fun)()
现在装饰器的内容基本差不多了。 还有一些问题, 可以自己去尝试研究。
自己的才是自己的
还有几个问题如下:
1. 类装饰器(装饰器装饰的对象是类)
2. 类函数装饰器(装饰器装饰的对象是类的函数)
3. 多个装饰器一起使用(函数嵌套)
https://docs.python.org/2/library/functools.html
转载来源:http://blog.csdn.net/TangHuanan/article/details/45094497
最后还是决定好好研究下。
先看看一些实例, 然后再来分析下原理
假设我们有如下的基本函数
def do_something(): for i in range(1000000): pass print "play game" do_something()结果如下:
play game需求1: 统计函数的执行时间
1. 不是装饰器的装饰器
import time def decorator(fun): start = time.time() fun() runtime = time.time()-start print runtime def do_something(): for i in range(1000000): pass print "play game" decorator(do_something)结果如下:
play game 0.0299999713898这种实现看上去还可以,但是每次调用的是decorator,还要把函数作为一个参数传入。这样需要修改调用的地方,使用起来就不方便了。
2. 最简单的装饰器
import time结果如下:
def decorator(fun):
def wrapper():
start = time.time()
fun()
runtime = time.time()-start
print runtime
return wrapper
@decorator
def do_something(): for i in range(1000000): pass print "play game" do_something()
play game 0.0329999923706装饰器是在函数定义时前面加@,然后跟装饰器的实现函数。可以看出,现在只要直接调用do_something就可以了。调用的地方不要作任何修改。
3. 目标函数带固定参数的装饰器
import time def decorator(fun): def wrapper(name): start = time.time() fun(name) runtime = time.time()-start print runtime return wrapper @decorator def do_something(name): for i in range(1000000): pass print "play game " + name do_something("san guo sha")结果如下:
play game san guo sha 0.039999961853实现很简单, 就是给wrapper函数参加相同的参数
4. 目标函数带不固定参数的装饰器
import time def decorator(fun): def wrapper(*args, **kwargs): print "args=",args print "kwargs=",kwargs start = time.time() fun(*args, **kwargs) runtime = time.time()-start print runtime print return wrapper @decorator def do_something(name): for i in range(1000000): pass print "play game " + name @decorator def do_something2(user, name): for i in range(1000000): pass print user+" play game " + name @decorator def do_something3(user, name, **kwargs): for i in range(1000000): pass print user+" play game " + name print kwargs do_something("san guo sha") do_something2("wang xiao er","san guo sha") mydict={"status":"ok"} do_something3("wang xiao er",'san guo sha', **mydict) ~结果如下:
args= ('san guo sha',) kwargs= {} play game san guo sha 0.0522739887238 args= ('wang xiao er', 'san guo sha') kwargs= {} wang xiao er play game san guo sha 0.0436179637909 args= ('wang xiao er', 'san guo sha') kwargs= {'status': 'ok'} wang xiao er play game san guo sha {'status': 'ok'} 0.0371160507202需求2: 目标函数每次调用重复执行指定的次数
5. 让装饰器带参数
import time def decorator(max): def _decorator(fun): def wrapper(*args, **kwargs): start = time.time() for i in xrange(max): fun(*args, **kwargs) runtime = time.time()-start print runtime return wrapper return _decorator @decorator(2) def do_something(name): fo 4000 r i in range(1000000): pass print "play game " + name do_something("san guo sha")结果如下:
play game san guo sha play game san guo sha 0.0600001811981
6. 原理
看了这么多实例, 装饰器的基本类型也基本上都有了。是不是清楚了呢?如果还是不清楚,那就继续看下面的内容。
1 不带参数的装饰器
@a_decorator def f(...): ... #经过a_decorator后, 函数f就相当于以f为参数调用a_decorator返回结果。 f = a_decorator(f)来分析这个式子, 可以看出至少要满足以下几个条件
1. 装饰器函数运行在函数定义的时候
2. 装饰器需要返回一个可执行的对象
3. 装饰器返回的可执行对象要兼容函数f的参数
2 验证分析
1 装饰器运行时间
import time def decorator(fun): print "decorator" def wrapper(): print "wrapper" start = time.time() fun() runtime = time.time()-start print runtime return wrapper @decorator def do_something(): for i in range(1000000): pass print "play game"结果如下:
decorator可以看出, 这里的do_something并没有调用, 但是却打印了decorator, 可wrapper没有打印出来。也就是说decorator是在do_something调用的时候执行的。
2 返回可执行的对象
import time结果如下:
def decorator(fun):
print "decorator"
def wrapper():
print "wrapper"
start = time.time()
fun()
runtime = time.time()-start
print runtime
return None
@decorator
def do_something(): for i in range(1000000): pass print "play game" do_something()
decoratorTraceback (most recent call last): File "deco.py", line 17, in <module> do_something() TypeError: 'NoneType' object is not callable
3 兼容函数f的参数
import time def decorator(fun): print "decorator" def wrapper(): print "wrapper" start = time.time() fun() runtime = time.time()-start print runtime return wrapper @decorator def do_something(name): for i in range(1000000): pass print "play game" do_something("san guo sha")结果如下:
decoratorTraceback (most recent call last): File "deco.py", line 17, in <module> do_something("san guo sha") TypeError: wrapper() takes no arguments (1 given)看到这里, 至少对不带参数的装饰器应该全弄清楚了, 也就是说能到看山还是山了。
3 带参数的装饰器
这里就给一个式子, 剩下的问题可以自己去想@decomaker(argA, argB, ...) def func(arg1, arg2, ...): pass #这个式子相当于 func = decomaker(argA, argB, ...)(func)
4 被装饰过的函数的函数名
import time def decorator(fun): def wrapper(): start = time.time() fun() runtime = time.time()-start print runtime return wrapper @decorator def do_something(): print "play game" print do_something.__name__结果如下:
wrapper可以看出, do_something的函数名变成了wrapper,这不是我们想要的。原因估计各位也都清楚了。那要怎么去解决呢?
import time def decorator(fun): def wrapper(): start = time.time() fun() runtime = time.time()-start print runtime wrapper.__name__ = fun.__name__ return wrapper @decorator def do_something(): print "play game" print do_something.__name__结果如下:
do_something但是这个看起来是不是很不专业, python的unctools.wraps提供了解决方法
import time import functools def decorator(fun): @functools.wraps(fun) def wrapper(): start = time.time() fun() runtime = time.time()-start print runtime return wrapper @decorator def do_something(): print "play game" print do_something.__name__结果如下:
do_something到此为止, 你是不是觉得已经完全明白了呢?
但事实是, 这其实还不够
7. 装饰器类
需求3: 让函数只能运行指定的次数前面我们讲的都是函数式的装饰器, 那么类能不能成为装饰器呢?
import time import functools class decorator(object): def __init__(self, max): self.max = max self.count = 0 def __call__(self, fun): self.fun = fun return self.call_fun def call_fun(self, *args, **kwargs): self.count += 1 if ( self.count == self.max): print "%s run more than %d times"%(self.fun.__name__, self.max) elif (self.count<self.max): self.fun(*args, **kwargs) else: pass @decorator(10) def do_something(): print "play game" @decorator(15) def do_something1(): print "play game 1" for i in xrange(20): do_something() do_something1()结果如下:
play game play game 1 play game play game 1 play game play game 1 play game play game 1 play game play game 1 play game play game 1 play game play game 1 play game play game 1 play game play game 1 do_something run more than 10 times play game 1 play game 1 play game 1 play game 1 play game 1 do_something1 run more than 15 times是不是感觉有点怪, 但它确实是可行的。
在Python中, 其实函数也是对象。 反过来, 对象其实也可以像函数一样调用, 只要在类的方法中实现__call__方法。回想一下创建对象的过程
class A: def __init__(self): pass a = A()这其实和函数调用没什么区别, 那么把这个式子代入到之前两个装饰器的式子中,结果如下:
带参数的装饰器
fun = A.__init__(args)(fun)
不带参数的装饰器
fun = A.__init__(fun)()
现在装饰器的内容基本差不多了。 还有一些问题, 可以自己去尝试研究。
自己的才是自己的
还有几个问题如下:
1. 类装饰器(装饰器装饰的对象是类)
2. 类函数装饰器(装饰器装饰的对象是类的函数)
3. 多个装饰器一起使用(函数嵌套)
8 参考资料
https://www.python.org/dev/peps/pep-0318/https://docs.python.org/2/library/functools.html
转载来源:http://blog.csdn.net/TangHuanan/article/details/45094497
相关文章推荐
- 深入理解Python 装饰器(decorator)
- 装饰器学习(2)------深入理解Python 装饰器(decorator)
- 深入理解Python装饰器
- 深入理解Python装饰器
- 深入理解Python中装饰器的用法
- 深入理解Python装饰器
- 深入理解python中的闭包和装饰器
- 对Python中装饰器(Decorator)的理解与进阶
- 深入理解python装饰器和闭包
- 深入理解Python中各种方法的运作原理(静态方法、类方法、抽象方法)
- 装饰器 decorator (python)
- 深入理解 Python 异步编程(上)
- python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域
- python深入学习笔记5-装饰器
- 深入理解Python中各种方法的运作原理
- 12步教你理解Python装饰器
- 深入理解 Python 异步编程(上)
- 全面深入彻底理解Python切片操作【原创】
- Python单元测试——深入理解unittest
- 机器学习之深入理解神经网络理论基础、BP算法及其Python实现