一,什么是装饰器
本质就是函数,功能是为其他函数添加附加功能
原则:
1,不修改被修饰函数的源代码
2,不修改被修饰函数的调用方式
例子:
import time def timmer(func): def wapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('函数的运行时间 %s' % (stop_time - start_time)) return res return wapper @timmer #加装饰器 def cal(l): #原函数 res = 0 time.sleep(1) for i in l: res += i return res t1 = cal(range(100)) print(t1) '''输出: 函数的运行时间 1.0000572204589844 4950 '''
二,装饰器的知识储备
装饰器 = 高阶函数 + 函数嵌套(闭包)
(一)高阶函数
定义:
1,函数接受的参数是一个函数名
2,函数的返回值是一个函数名
3,满足上述条件任意一个,都可称之为高阶函数
例子:高阶函数
def foo(): print('你好啊林师傅') def test(func): print(func) #打印内存地址 func() #执行函数foo test(foo) #test是高阶 '''输出: <function foo at 0x00000000022F22F0> 你好啊林师傅 '''
例子:函数接受的参数是一个函数名,就能实现给函数增加功能的目标
def foo(): time.sleep(3) print('你好啊林师傅') def test(func): start_time = time.time() func() #执行函数foo stop_time = time.time() print('函数的运行时间 %s' % (stop_time - start_time)) test(foo) #修改了函数原来的调用方式,原来是foo() '''输出: 你好啊林师傅 函数的运行时间 3.000171661376953 '''
例子:如果再加上返回值是一个函数,则能实现“不修改被修饰函数的调用方式”这一个条件
def foo(): print('from the foo') def test(func): return func # res = test(foo) # print(res) #res得到的是foo的内存地址 # res() #实际是运行foo foo = test(foo) #如果把函数的执行结果重新赋值给foo函数名 foo() #就能实现“不修改被修饰函数的调用方式”这一个条件 '''输出: from the foo '''
例子:多运行一次,并不合格(高阶函数并不能全部满足装饰器的需要) 1,不修改foo源代码,2,不修改foo调用方式
def foo(): time.sleep(3) print('from the foo') def timer(func): start_time = time.time() func() #执行函数foo stop_time = time.time() print('函数的运行时间 %s' % (stop_time - start_time)) return func foo = timer(foo) foo() #又执行了一次函数foo '''输出: from the foo 函数的运行时间 3.007171869277954 from the foo '''
(二),函数嵌套
定义:在函数里面定义另一个函数
def bar(): print('from bar') def foo(): print('from foo') def test(): pass
函数闭包:
闭——封装变量,包——层层嵌套。其实和函数作用域是一回事。特性:如果内层没有定义重名变量,外层的变量可以传进来。
def father(auth_type): # print('from father %s' %name) def son(): # name='linhaifeng_1' # print('我的爸爸是%s' %name) def grandson(): print('我的爷爷是%s' %auth_type)#实参'filedb'从最外层一直传导进来 grandson() # print(locals()) son() # father('linhaifeng') father('filedb')
#输出:我的爷爷是filedb
三,装饰器框架
使用高阶函数+函数闭包完成装饰器框架。
个人理解:1,装饰器函数传入的原函数名func,用在最内层运行原函数,以实现和原函数一样的效果
2,而在原函数外层包裹的一层函数wrapper,加retunrn wapper返回的是加入新功能后的一个函数地址,在外面运行这个函数地址也就执行了加入的新功能
3,而@这样一个语法把新函数的地址赋值给原函数名,这样运行原函数名,就把新的功能增加了。
def timmer(func): #func=test def wrapper(): # print(func) start_time=time.time() func() #就是在运行test() stop_time = time.time() print('运行时间是%s' %(stop_time-start_time)) return wrapper @timmer #test=timmer(test) def test(): time.sleep(3) print('test函数运行完毕') test() '''输出: test函数运行完毕 运行时间是3.000171661376953 ''' #先使用res来接收 # res=timmer(test) #返回的是wrapper的地址 # res() #执行的是wrapper() #然后使用test原函数名替换res # test=timmer(test) #返回的是wrapper的地址 # test() #执行的是wrapper() #最后python提供@语法等效于test=timmer(test) # @timmer 就相当于 test=timmer(test)
例子:原函数加入返回值情况
import time def timmer(func): def wrapper(): start_time=time.time() res=func() #使用res接收返回值 stop_time = time.time() print('运行时间是%s' %(stop_time-start_time)) return res #把返回值传回去 return wrapper @timmer def test(): time.sleep(3) print('test函数运行完毕') return '这是test的返回值' #这里原函数test加入了返回值 res=test() print(res) '''输出: test函数运行完毕 运行时间是3.007171869277954 这是test的返回值 '''
例子:加入参数,并且考虑原函数参数不固定的情况
#传参的复习和回顾 def test2(name,age,gender): #test2(*('alex',18,'male','x','y'),**{}) #name,age,gender=('alex',18,'male','x','y') print(name) print(age) print(gender) def test1(*args,**kwargs): test2(*args,**kwargs) #args=('alex',18,'male','x','y') kwargs={} test1('alex',18,gender='male') #正确的传参方式 test1('alex',18,'male') #正确的传参方式 test1('alex',18,'male','x','y')#多传了两个参数,报错:test2() takes 3 positional arguments but 5 were given
#(*args,**kwargs)实现了传递可变参数的功能,在原函数有多个变体的情况下,装饰器函数可以通用 import time def timmer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) #可以理解为wrapper得到的参数原封不动的传进原函数 print('args 是 %s:' %(args,)) print('kwargs 是: %s' %kwargs) stop_time = time.time() print('运行时间是%s' %(stop_time-start_time)) return res return wrapper @timmer def test(name,age): #两个参数 time.sleep(1) print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age)) return '这是test的返回值' @timmer def test1(name,age,gender): #三个参数 time.sleep(1) print('test1函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender)) return '这是test1的返回值' test('linhaifeng',age=18) #args=('linhaifeng') kwargs={'age':18} print('######') test1('alex',18,'male') #args=('alex',18,'male') kwargs={}
'''输出: test函数运行完毕,名字是【linhaifeng】 年龄是【18】 args 是 ('linhaifeng',): kwargs 是: {'age': 18} 运行时间是1.0000572204589844 ###### test1函数运行完毕,名字是【alex】 年龄是【18】 性别【male】 args 是 ('alex', 18, 'male'): kwargs 是: {} 运行时间是1.0000572204589844 '''
两个小技巧
#1,解压序列 a,*_,c =[10,9,8,7,6]#代表所有值 print(a) print(_) #下划线_是变量 print(c) '''输出: 10 [9, 8, 7] 6 '''
#2,交换两个变量值 f1 = 1 f2 = 10 f1,f2 = f2,f1 print(f1,f2) #输出:10 1
四,装饰器实战
(一)使用装饰器实现验证和session功能
user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth_func(func): def wrapper(*args,**kwargs): if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用户名:').strip() passwd=input('密码:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: #######这个else非常有价值,不是if的else,而是for的else print('用户名或者密码错误') return wrapper @auth_func def index(): print('欢迎来到京东主页') @auth_func def home(name): print('欢迎回家%s' %name) @auth_func def shopping_car(name): print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) print('before-->',current_dic) index() print('after--->',current_dic) home('产品经理') # shopping_car('产品经理')
五,装饰器运行流程
1,运行@装饰器函数名,实际动作是运行装饰器函数,由于闭包函数wrapper定义在这个装饰器函数的里面,所以仅return wrapper,把wrapper的函数地址返回给原函数名(重新赋值)
2,当程序运行到原函数名(),也就是执行原函数时,因为前面的步骤,实际跳转到wrapper函数执行。这里面巧妙的地方是,wrapper函数里面又包含一个原函数(),这回是真正执行原函数()。
3,执行完原函数后,回到wrapper里面,原函数()下面的步骤运行。
六,带参数的装饰器函数
本质就是在外面再添加一层,把原装饰器函数包裹起来。原装饰器函数实现的是把原函数名传递进去在内层,带参数的装饰器函数实现传递功能性参数作用在最外层,层层嵌套。
user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth(auth_type='filedb'): def auth_func(func): def wrapper(*args,**kwargs): print('认证类型是',auth_type) if auth_type == 'filedb': if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用户名:').strip() passwd=input('密码:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: print('用户名或者密码错误') elif auth_type == 'ldap': print('鬼才特么会玩') res = func(*args, **kwargs) return res else: print('鬼才知道你用的什么认证方式') res = func(*args, **kwargs) return res return wrapper return auth_func @auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type --->index=auth_func(index) def index(): print('欢迎来到京东主页') @auth(auth_type='ldap') def home(name): print('欢迎回家%s' %name) # @auth(auth_type='sssssss') def shopping_car(name): print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) # print('before-->',current_dic) # index() # print('after--->',current_dic) # home('产品经理') shopping_car('产品经理')