函数 (三) 装饰器
一 为何要用装饰器
有的时候写完一段代码,过段时间需要对它进行升级、添加一些新功能,但是如果要直接修改原来的代码会影响其他人的调用,所以就需要一个不修改源代码且不修改原函数的调用方式的东西又能为原函数增添新功能的东西,装饰器就是干这个的。
二 什么是装饰器
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。 强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式 装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
开放封闭原则:对修改封闭,对扩展开放
三 装饰器的使用
下面是为一个函数添加装饰器,添加了计算其运行时间的功能:
1 import time 2 def timmer(func): 3 def wrapper(*args,**kwargs): 4 start_time=time.time() 5 res=func(*args,**kwargs) 6 stop_time=time.time() 7 print('run time is %s' %(stop_time-start_time)) 8 return res 9 return wrapper 10 11 @timmer 12 def foo(): 13 time.sleep(3) 14 print('from foo') 15 foo()
四、装饰器语法及固定格式
1 def 装饰器函数名(func): 2 def wrapper(*args,**kwargs): 3 ret = func(*args,**kwargs) 4 return ret 5 return wrapper 6 7 @装饰器函数名 8 def func(): 9 pass
五、应用练习
1 #######################################作业练习####################################################### 2 # 3 # 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码 4 # user_exist = [False] 5 # def auth(func): 6 # def wrapper(*args,**kwargs): 7 # #注册功能 8 # with open('db.txt','r',encoding='utf-8') as f: 9 # user_dic = eval(f.read()) 10 # flag = False 11 # while not user_exist[0]: 12 # username = input('请输入您的用户名:').strip() 13 # password = input('请输入您的密码:').strip() 14 # if username in user_dic and password == user_dic[username]: 15 # print('恭喜您,登录成功!') 16 # user_exist[0] = True 17 # break 18 # else: 19 # print('账号或密码错误,请重新输入!') 20 # ret = func(*args,**kwargs) 21 # return ret 22 # return wrapper 23 # 24 # @auth 25 # def func1(): 26 # print('函数1') 27 # @auth 28 # def func2(x): 29 # print('函数2',x) 30 # func1() 31 # func2(111111) 32 # 2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名称写入文件 33 # def log(func): 34 # def wrapper(*args,**kwargs): 35 # with open('db2.txt','a',encoding='utf-8') as f: 36 # f.write('%s函数正在被调用。\n'%func.__name__) 37 # ret = func(*args,**kwargs) 38 # return ret 39 # return wrapper 40 # @log 41 # def func1(): 42 # print('func1函数被调用了。。。。') 43 # @log 44 # def func2(): 45 # print('func2函数被调用了。。。。') 46 # 47 # func1() 48 # func2() 49 # 进阶作业(选做): 50 # 1.编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 51 from urllib.request import urlopen 52 53 # def get_html(url): 54 # print(urlopen(url).read()) 55 # 56 # get_html('http://www.baidu.com') 57 # 2.为题目1编写装饰器,实现缓存网页内容的功能: 58 # 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 59 # from urllib.request import urlopen 60 # 61 # def get_bak(func): 62 # def wrapper(*args,**kwargs): 63 # with open('html.bak','r+',encoding='utf-8') as f: 64 # if not f.read(): 65 # ret = func(*args, **kwargs) 66 # print(ret) 67 # f.write(ret.decode('utf-8')) 68 # else: 69 # print('以下内容是从缓存中获得的^^^^^') 70 # f.seek(0) 71 # print(f.read()) 72 # ret = func(*args, **kwargs) 73 # return ret 74 # return wrapper 75 # 76 # @get_bak 77 # def get_html(url): 78 # return urlopen(url).read() 79 # get_html('http://www.python.org')