装饰器的深入
1. 装饰器的使用法则
开放封闭原则:
1.对扩展是开放的
为什么要对扩展开放呢?
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2.对修改是封闭的
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
2. 装饰器的主要功能和装饰器的固定结构
装饰器的主要功能
在不改变函数调用方式的基础上在函数的前、后添加功能。
装饰器的固定格式
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner
装饰器的固定格式wraps版--后续了解
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper 装饰器的固定格式——wraps版
3. 装饰器基础需求-计算一个函数的运行时间
见博文
http://www.cnblogs.com/wangph/p/8875544.html
计算一个函数的运行时间 import time def timmer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print(end_time - start_time) return ret return inner @timmer # func = timmer(func) def func(a,b): print('begin func',a) time.sleep(0.1) print('end func',b) return True ret = func(1,2) #--> inner() def timmer(f): def inner(*args,**kwargs): ret = f(*args,**kwargs) return ret return inner
4. 装饰器进阶需求
4.1 确认是否启用装饰器-装饰器的参数
如何确认你的装饰器是否生效呢?最好的方式 是传参决定
我们的方法是,在原装饰器的外层,再加一层函数,然后return原装饰器的函数名。
之后在装饰器内层函数中加判断,确认参数的质量是否生效。
import time FLAG = True def outer(flag): #在装饰器timmer的外层加outer函数,接收传参 flag def timmer(f): def inner(*args,**kwargs): if flag == True: ##在装饰器的中的内层函数inner中判断传参,确认装饰器是否生效 start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print(end_time - start_time) else: ret = f(*args, **kwargs) return ret return inner return timmer # outer函数中返回装饰器的函数名timmer @outer(FLAG) # func = timmer(func) #调用装饰器,调用outer即可,在后面加实参 def func(a,b): print('begin func',a) time.sleep(0.1) print('end func',b) return True func(1,2)
4.2 一个函数使用多个装饰器来装饰
eg:一个函数需要同时使用确认登录和计算运行时间的装饰器
现在我已经有了如下两个装饰器和一个函数,那么我们如何调用这两个装饰器对着一个函数进行装饰呢?
import time login_info = {'alex':False} def login(func): # manager def inner(name): if login_info[name] != True: user = input('user :') pwd = input('pwd :') if user == 'alex' and pwd == 'alex3714': login_info[name] = True if login_info[name] == True: ret = func(name) # timmer中的inner return ret return inner def timmer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) # 调用被装饰的方法 end_time = time.time() # print(end_time - start_time) return ret return inner def index(name): print('欢迎%s来到博客园首页~'%name)
使用多个装饰器的方法
使用方法一:
@timmer @login def index(name): print('欢迎%s来到博客园首页~'%name)
执行结果
user :alex
pwd :alex3714
欢迎alex来到博客园首页~
6.77495002746582
使用方法二:
@login @timmer def index(name): print('欢迎%s来到博客园首页~'%name)
执行结果呢
user :alex
pwd :alex3714
欢迎alex来到博客园首页~
3.0994415283203125e-05
我们发现两种调用装饰器的方法差异是:两个装饰器的上下顺序不相同。
返回的结果是第二张方法timmer计算了函数index的执行时间,而第一个方法timmer则计算了index的执行时间和装饰器login的运行时间
由此我们可以发现,使用装饰器的时候,装饰器对它正下方挨着的函数(装饰器)生效 !
其中的函数执行关系我们可以通过下面一段代码来了解
def wrapper1(func): def inner1(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner1 def wrapper2(func): def inner2(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner2 @wrapper2 @wrapper1 def f(): print('in f') f()
让我们来看一下这段函数的执行返回结果
wrapper2 ,before func wrapper1 ,before func in f wrapper1 ,after func wrapper2 ,after func
我们来看图分析下当函数f使用了两个装饰器的时候,它的执行过程:
''' 根据上面的分析,当f调用两个装饰器的时候,首先执行的是最上面装饰器的内层函数 iunner2 这时候到了执行阶段 执行inner2 第一步 print('wrapper2 ,before func') 第二步 func() = inner1() 执行inner1的第一步 print('wrapper1 ,before func') 第二步 func() = f 执行 f中的主函数 print('in f') 第三步 执行 inner1的 print('wrapper1 ,after func') 第三步 func() = inner1() inner()1执行完了,func()就执行完了。继续往下走 执行 print('wrapper2 ,after func') 函数执行结束 在这个过程中要注意语法糖中的变量指向和装饰器中func接收的实参变化 '''