python-大话装饰器
装饰器
装饰器是个什么玩意呢?是个会了不难,装逼神器。但是如果不了解理解起来还是很抽象,让我们试试这个装逼神器吧!
1.什么是装饰器
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
2.需求是怎么来的
装饰器的定义很是抽象,先写一个无聊的小程序
1 def foo(): 2 print 'in foo()' 3 4 foo()#我擦这是什么鬼,谁不会啊,装13呢吧?
是不是觉得写这个程序很二,简单的不能再简单了?但是突然又来了一个更无聊的人B君,说我要看看执行这个函数用了多少时间,让你来满足B君的需求,你是不是觉得太简单,showtime。。。。
1 import time 2 def foo(): 3 startTime = time.clock() 4 print 'in foo()' 5 endTime = time.clock() 6 print 'usedtime:',endTime - startTime 7 8 foo()
ok,你可以傲娇的告诉B君,自己看吧,但是B君说,小伙子干的不错,我又想看看foo1和foo2这两函数的执行时间,这时候你怎么办?和刚才一样把你在foo上加的代码全部加到foo1和foo2上?但是如果B君说,我后边还有3,4,5.。。。等1w+个函数我都要看,你这时候怎么搞?全部复制一遍?还是直接干他丫的?
先来点小常识:
def home(): print 'this is the home page!' home() print home a = home a() #输出: this is the home page! <function home at 0x0000000002C890B8> this is the home page!
这个地方我们定义了一个函数,大家都知道:
1.home(),python 就会执行了这个函数,输出了‘this is the home page!’,
2.你看到了实例的地方,如果我不加()这个地方就是一个home函数在内存的存储地址。
3.如果我把这个地址赋值给另一个变量a,之后我a()函数同样执行了,结果输出和home()一样。函数地址可以作为函数的参数
解决问题1:
1 import time 2 3 def foo(): 4 print 'in foo()' 5 6 def timeit(func): #你定义了一个函数专门干计算执行时间这个事情 7 start = time.clock() 8 func()#上面讲到了函数是可以当参数传过来的,在加上()是可以执行 9 end =time.clock() 10 print 'used:', end - start 11 12 timeit(foo) #通过这个方法似乎不管是多少个函数你只要把它当参数都可以解决了
似乎逻辑上解决了B君的需求,一切都是很美好的运行着!。。。等等,突然B君发现我们现在的调用方式似乎发生了变化,之前我们是foo(),现在我们变成了timeit(foo),B君说:你这不还是得需要添加这行代码么,我现在更变态了,我有100w+个函数要看,而且我还在不同的模块内放着,涉及到了10w+个文件,你去改吧!无耻的奸笑声。。。。我似乎听到B君小声叨咕:我弄不死你小样的。
解决问题2:
人活着就是为了争一口气,不争馒头争口气,我们只能想办法在不修改调用的代码情况下解决问题,如果不修改代码,也就是意味着调用的时候仍然为foo(),但是我们还需要达到第类似timeit(foo)的效果。根据上面小常识中提到的如果我吧timeit赋值给foo,是不是执行foo()的效果和timeit()的效果一样?等等,这个地方似乎timeit()里面还传递了一个参数,我们得想办法把参数统一吧,如果timeit(foo)不是直接产生调用效果,而是返回一个和foo参数列表一致的函数的话就解决问题了,将timeit(foo)的返回赋值给foo,然后调用foo()的代码完全不用修改。
1 #-*- coding: UTF-8 -*- 2 import time 3 4 def foo(): 5 print 'in foo()' 6 7 # 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法 8 def timeit(func): 9 10 # 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装 11 def wrapper(): 12 start = time.clock() 13 func() 14 end =time.clock() 15 print 'used:', end - start 16 17 # 将包装后的函数返回 18 return wrapper #返回一个可调用的内存地址这样就可以被调用了 19 20 foo = timeit(foo) 21 foo()
这样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念。上面这段代码看起来似乎已经不能再精简了,Python于是提供了一个语法糖来降低字符输入量。
1 import time 2 3 def timeit(func): 4 def wrapper(): 5 start = time.clock() 6 func() 7 end =time.clock() 8 print 'used:', end - start 9 return wrapper 10 11 @timeit 12 def foo(): 13 print 'in foo()' 14 15 foo()
到这个地方你已经完美的解决了问题,同时你也在不知不觉中明白了装饰器了如果现在这个函数中待上参数怎么办?
解决函数中带参数的问题3:
现在foo(avg)中带参数了改怎解决呢,因为我们已经知道了foo其实已经不是之前的foo了,之前的foo已经当参数传给了timeit函数了,func=之前的foo,而现在是foo是wrapper
因此foo()中有多少个参数,我们响应在wrapper()中对应加上就可以了!
执行顺序解析:
解析分解
似乎又出现了这种多变的问题了额,还好python已经帮我们想到了
1 import time 2 ###############一个参数######################## 3 def timeit(func): 4 def wrapper(arg): 5 start = time.clock() 6 func(arg) 7 end =time.clock() 8 print 'used:', end - start 9 return wrapper 10 11 @timeit 12 def foo(arg): 13 print 'in foo()' 14 15 foo('sdasd') 16 17 18 ##############二两参数######################### 19 def timeit(func): 20 def wrapper(arg1,arg2): 21 start = time.clock() 22 func(arg1,arg2) 23 end =time.clock() 24 print 'used:', end - start 25 return wrapper 26 @timeit 27 def foo(arg1,arg2): 28 print 'in foo()' 29 30 foo('a','b') 31 32 #############三两参数######################### 33 34 def timeit(func): 35 def wrapper(arg1,arg2,arg3): 36 start = time.clock() 37 func(arg1,arg2) 38 end =time.clock() 39 print 'used:', end - start 40 return wrapper 41 @timeit 42 def foo(arg1,arg2,arg3): 43 print 'in foo()' 44 45 foo('a','b','c')
动态参数一步搞定,以后你可以直接用这种情况就行了,不用再考虑参数的个数,相当于万能的吧!
1 def timeit(func): 2 def wrapper(*args,**kwargs): 3 start = time.clock() 4 func(*args,**kwargs) 5 end =time.clock() 6 print 'used:', end - start 7 return wrapper 8 @timeit 9 def foo(arg1,arg2,arg3): 10 print 'in foo()' 11 12 foo('a','b','c','d')
多个装饰器共用问题4:
多个装饰器的情况下,你可以理解成这个一个俄罗斯套娃,是一层一层的,代码从上到下,套娃是从外到里。注意一定要明白执行顺序。
1 def wrapper2(func): 2 def inner(): 3 print 'w2,before' 4 func() 5 print 'w2,after' 6 return inner 7 8 def wrapper1(func): 9 def inner(): 10 print 'w1,before' 11 func() 12 print 'w1,after' 13 return inner 14 15 def wrapper0(func): 16 def inner(): 17 print 'w0,before' 18 func() 19 print 'w0,after' 20 return inner 21 22 @wrapper2 23 @wrapper1 24 @wrapper0 25 26 def foo(): 27 print 'foo' 28 29 foo()
带有参数的装饰器5:
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 4 def Filter(a1,a2): 5 def outer(main_func): 6 print 'main_func=',main_func 7 def wrapper(request,kargs): 8 print a1 9 main_result = main_func(request,kargs) 10 print a2 11 return main_result 12 return wrapper 13 return outer 14 f5 = 123 15 f6=123 16 @Filter(f5, f6) 17 def Index(request,kargs): 18 print 'index' 19 print dir(Filter) 20 Index(1,2)
1.执行最外层的函数Filter,同时将两个参数传入进去,并得到返回值,outer函数地址。
2.创建装饰器,@+outer组合成@outer装饰器,这就和没有参数的装饰器一样了。
3.执行outer函数。
4.将outer函数的返回值赋值给被装饰的函数的函数名。