对于装饰器来说,就是在不改变函数的调用的情况下,对函数的前后增加了些许功能,这完全符合函数的 开放封闭 原则。装饰器的本质 其实就是一个闭包函数。
这是一个装饰器的步骤图
def wrapper(func): # 2 def inner(*args,**kwargs): # 5 ret = func(*args,**kwargs) # 6 return ret #10 return inner # 3 @wrapper #shopping = wrappers(shooping) # 1 --shopping = innner def shopping(num): #7 print(num) # 8 return 1 # 9 print(shopping(5)) # 4
这样你调 shopping 时, 真实情况是你在调用 inner 函数。如果 你想打印其函数名时打印的其实是 innner 函数。
from functools import wraps def wrapper(func): # 2 @wraps(func) def inner(*args,**kwargs): # 5 ret = func(*args,**kwargs) # 6 return ret #10 return inner # 3 @wrapper #shopping = wrappers(shooping) # 1 --shopping = innner def shopping(num): #7 print(num) # 8 return 1 # 9 print(shopping(5)) # 4 print(shopping.__name__) #以字符串的形式获取到函数名
print(shopping.__doc__) #以字符串的形式获取到函数注释
如果用内置的模块,wraps ,就可以轻松解决这个问题,使得所有的还和以前一样,其中wraps(),中要传入参数,参数应该与外层装饰器的形参一致。
对于装饰器,如果你想取消个这装饰器的功能,现在给出的方法就只能是在装饰的外面在套一个装饰器,并设置一个定位符(一个全局变量)来控制其是否执行:
control = True def outer(flag): def wrapper(func): def inner(*args,**kwargs): if flag: print('函数执行前') ret = func(*args,**kwargs) print('函数执行后') return ret else: ret = func(*args, **kwargs) return inner return wrapper @outer(control)# shoping = outer(shopping) = wapper(shoping) = inner(shopping) 只是要多一个参数进行判断 def shopping(num): print(num) return 1 shopping(5)
多个装饰器进行嵌套,装饰器的糖的运行是,就近原则,离函数最近的糖先运行
def wrapper1(func): #func--shopping def inner(*args,**kwargs): print('000函数执行前') ret = func(*args,**kwargs) print('000函数执行后') return ret return inner def wrapper2(func): #func--inner1 def inner(*args,**kwargs): print('111函数执行前') ret = func(*args,**kwargs) #这里的调用其实是使用 inner1() 函数 print('111函数执行后') return ret return inner @wrapper2 @wrapper1 def shopping(num): print(num) return 1 shopping(5) ''' 111函数执行前 000函数执行前 5 000函数执行后 111函数执行后 '''
来个作业
# 在12 天作业 编写装饰器,为多个函数加上认证功能(用户的账号来源于文件), # 要求登入一次成功,后续的函数都无需再输入用户和密码 def wrapper(func): def inner(*args, **kwargs): with open('ver', mode='r+',encoding='utf-8')as f: flag = f.read() if flag: verification = input('>>>').strip() if verification == 'eli123': # f.seek(0) f.truncate(0,) ret = func(*args, **kwargs) return ret else: ret = func(*args, **kwargs) return ret return inner @wrapper def buy(num): print('买了{}个包子'.format(num)) @wrapper def eat(num): print('吃了{}个包子'.format(num)) with open('ver',mode='w',encoding='utf-8') as f: f.write('aaa') buy(3) eat(2)
这里利用了一个文件,在进行函数前,生成内容,然后文件中有内容时,要求验证 验证码,然后删除,方便第二次的验证。
# 2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名称写入文件 from functools import wraps def wrapper(func): wraps(func) def inner(*args, **kwargs): with open('record',mode='a',encoding='utf-8') as f: f.write(func.__name__ + ' ') ret = func(*args, **kwargs) return ret return inner @wrapper def buy(num): print('买了{}个包子'.format(num)) @wrapper def eat(num): print('吃了{}个包子'.format(num)) buy(3) eat(2)
与第一题相比,确实比较容易一点(之后学到时间模块,可以加入时间模块的)