今天我们准备学习函数的装饰器,其中包括楔子,装饰器的形成过程和主要功能、装饰器的固定结构。
1.首先我们需要知道什么是装饰器?
装饰器本质就是函数,为其他函数提供新功能。
2.装饰器需要遵循的原则:(1)不修改被装饰函数的源代码(开放封闭原则)
(2)为被装饰函数添加新功能后,不修改被修饰函数的调用方法
3.装饰器形成的过程和主要功能
装饰器的本质:一个闭包函数
(1)形成过程:
import time def func1(): print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1()
由于赋值调用步骤较多,需要用到语法糖,具体代码如下:
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer #==> func1 = timer(func1) def func1(): print('in func1') func1()
(2)装饰器的功能:在不修改原函数及其调用方式的情况下对原函数的功能进行扩展
装饰器的固定格式
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner
4.无参装饰器
def timmer(): def wrapper(): func() return wrapper
5.带参装饰器
def func(func): def wrapper(*args,**rargs): func(*args,**kargs) return wrapper
加上功能
import time def timer(func): def wrapper(*args,**kwargs): start=time.time() func(*args,**kwargs) end=time.time print('')
带返回值的装饰器
import time def timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return inner @timer #==> func2 = timer(func2) def func2(a): print('in func2 and get a:%s'%(a)) return 'fun2 over' func2('aaaaaa','bbbbbb') print(func2('aaaaaa')) 装饰器—带返回值的装饰器
6.开放封闭原则
(1)对扩展是开放的
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
(2)对修改是封闭的
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
7.多个装饰器装饰同一个函数
有些时候,我们也会用到多个装饰器装饰同一个函数的情况。
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
练习:
''' 编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件), 要求登录成功一次,后续的函数都无需再输入用户名和密码 注意:从文件中读出字符串形式的字典, 可以用eval('{"name":"egon","password":"123"}')转成字典格式 ''' # user_dict = { # 'pang':'123456', # 'panda':'binbin', # 'nezha':'101010' # } # print(type(user_dic)) auth_status = { 'user':None, #设置全局的字典 'status':False } def auth(func): def inner(*args,**kwargs): if auth_status['status']: #如果auth_status为真,直接调用就行 ret = func(*args, **kwargs) # index/home return ret else: username = input('username : ').strip()#.strip()避免用户输入带有空格的用户名 password = input('password : ').strip() # 读文件获取用户信息 f = open('conf') #文件句柄 user_info = f.read() #读取user用户信息 user_dic = eval(user_info) #通过eval转换成字典 if user_dic.get(username) and user_dic[username] == password:#获取字典的k,确认有存在的用户名并找到对应的密码 print('login successful') auth_status['user'] = username auth_status['status'] = True ret = func(*args,**kwargs) #index/home 如果登录成功执行func return ret else: print('login failed') return inner @auth #装饰器 def index(): print("欢迎来到首页") @auth def home(): print("欢迎回家") index() index() home() index() # #认证功能 # #获取用户名和密码 # username = input('username : ').strip() # password = input('password : ').strip() # #读文件获取用户信息 # f = open('conf') # user_info = f.read() # user_dic = eval(user_info) # if user_dic.get(username) and user_dic[username] == password: # print('login successful') # else: # print('login failed')
''' 进阶作业(选做): 1.编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 2.编写装饰器,实现缓存网页内容的功能: 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0), 就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 ''' url_l = [] from urllib.request import urlopen def get_cache(func): def inner(*args,**kwargs): url = args[0] filename = str(hash(url)) if url in url_l: f = open(filename,'rb') ret = f.read() else: url_l.append(url) ret = func(*args, **kwargs) f = open(filename,'wb') f.write(ret) f.close() return ret return inner @get_cache def get(url): return urlopen(url).read() print(get('http://www.cnblogs.com/linhaifeng'))