一.什么是装饰器
顾名思义,装饰器指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
二.装饰器需要遵循的原则
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修改函数的调用方式
3.装饰器的本质 :闭包函数
功能:就是在不改变原函数调用方式的情况下,在这个函数前后加上扩展功能
示例1:
import time def timmer(func): #函数名可以当做函数的参数 def inner(): start = time.time() func() end = time.time() print(end - start) return inner def hahaha(): time.sleep(0.1) print('aaaa')
示例2:
def timmer(func): #---> hahaha def inner(x,y): func(x,y) # --->hahaha return inner def hahaha(a,b): time.sleep(0.1) print(a,b) def kkk(a): print(a)
三.装饰器的主要功能和装饰器的固定结构
装饰器的主要功能:
在不改变函数调用方式的基础上在函数的前、后添加功能。
装饰器的固定格式:
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner
语法糖
import time def wrapper(func): # 装饰 def inner(*args, **kwargs): start = time.time() ret = func(*args, **kwargs) end = time.time() print(end - start) return ret return inner @wrapper def lll(): time.sleep(0.1) print('hello') lll()
四.
1.带参数的装饰器
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') re = func(*args,**kwargs) if flag: print('''执行函数之后要做的''') return re return inner return timer @outer(False) def func(): print(111) func()
F = False def outer(flag): def wrapper(func): def inner(*args,**kwargs): if flag: print('before') ret = func(*args,**kwargs) print('after') else: ret = func(*args, **kwargs) return ret return inner return wrapper @outer(F) #-->@wrapper -->hahaha = wrapper(hahaha) #-->hahaha == inner def hahaha(): print('hahaha') @outer(F) #shuangww = outer(shuangww) def shuangww(): print('shuangwaiwai') shuangww() hahaha()
2.多个装饰器装饰同一个函数
def qqxing(func): #func = pipixia_inner def qqxing_inner(*args,**kwargs): print('in qqxing:before') ret = func(*args,**kwargs) #pipixia_inner print('in qqxing:after') return ret return qqxing_inner def pipixia(func): #dapangxie def pipixia_inner(*args,**kwargs): print('in pipixia:before') ret = func(*args,**kwargs) #dapangxie print('in pipixia:after') return ret return pipixia_inner #qqxing(pipixia_inner) -->dapangxie = qqxing_inner() @qqxing #dapangxie = qqxing(dapangxie) -->dapangxie = qqxing(pipixia(dapangxie)) --> @pipixia #dapangxie = pipixia(dapangxie) def dapangxie(): print("饿了么") dapangxie()
3.开放封闭原则
(1)对扩展是开放的
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
(2)对修改是封闭的
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
练习:
''' 编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件), 要求登录成功一次,后续的函数都无需再输入用户名和密码 注意:从文件中读出字符串形式的字典, 可以用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'))