-
什么是装饰器?
本质就是个函数,功能为其他函数添加附加功能 -
装饰器两个原则,不能动原来的函数
1)不修改被修饰函数的源代码
2)不修改被修饰函数的调用方式 -
知识储备
装饰器=高阶函数+函数嵌套+闭包 -
高阶函数
满足下列条件之一便可称为是高阶函数:
1)函数接受的参数是一个函数名;
可以实现为函数增加功能
#高阶函数
def foo():
print('你好呀,letty')
def test(fun):
start_time=time.time()
fun()
stop_time=time.time()
print('函数的运行时间是%s' %(stop_time-start_time))
test(foo) #增加了foo()的函数功能可以计时,但是改变了调用方式,一般的调用方式应该是foo()
结果:
你好呀,letty
函数的运行时间是0.0
2)函数的返回值是一个函数名
可以实现不改变函数的调用方式
def foo():
print('你好呀,letty')
def test1(fun):
return fun
foo=test1(foo)
foo() #这里foo仅是上边的变量名字
结果:
你好呀,letty
单高阶函数无法满足装饰器
- 函数嵌套
在函数内部定义函数
#套一层
def father(name):
print('from father %s' %name)
def son():...
print(locals()) #打印当前层的局部变量
father('alex')
结果:
from father alex
{'son': <function father.<locals>.son at 0x000002A44FB53268>, 'name': 'alex'}
上边这个结果说明son()是局部变量,那就只能在内部调用,不能在外部调用
def father(name):
print('from father %s' %name)
def son():
print('My father is %s' %name)
print(locals()) #打印当前层的局部变量
son()
结果:
from father alex
{'son': <function father.<locals>.son at 0x000002A44FB339D8>, 'name': 'alex'}
My father is alex
可以再多层嵌套
def father(name):
print('from father %s' %name)
def son():
print('My father is %s' %name)
def grandson():
myname='lll'
print('My grandfather is %s' %name)
grandson()
print(locals()) #打印当前层的局部变量
son()
father('alex')
结果:
from father alex
{'son': <function father.<locals>.son at 0x000002A44FB56268>, 'name': 'alex'}
My father is alex
My grandfather is alex
- 闭包
函数的嵌套
包:一层一个包
闭:封装,封装什么:封装变量 - 装饰器基本实现:
装饰器的框架
def timer(fun):
def wrapper():
print(fun)
fun()
return wrapper
如何用:
def timer(fun):
def wrapper():
print(fun)
fun()
return wrapper
def test111():
time.sleep(3)
print('test111函数运行完毕')
wrapper=timer(test111)
wrapper()
结果:
<function test111 at 0x000002A44FB56598>
test111函数运行完毕
给函数加计时功能
def timer(fun):
def wrapper():
star_time=time.time()
fun()
stop_time=time.time()
print('运行时间是%s' %(stop_time-star_time))
return wrapper
def test111():
time.sleep(3)
print('test111函数运行完毕')
test111=timer(test111)
test111()
结果:
test111函数运行完毕
运行时间是3.0148227214813232
上边代码就实现了增加新功能但是没有修改源码,没有改变函数调用方式,但是还有弊病:得赋值,把要增加功能的test111函数作为参数传过去
完善技巧
使用Python语法糖 : @
@timer 就相当于 test111=timer(test111)
def timer(fun):
def wrapper():
star_time=time.time()
fun()
stop_time=time.time()
print('运行时间是%s' %(stop_time-star_time))
return wrapper
@timer #修饰哪个函数,就在哪个函数加它
def test111():
time.sleep(3)
print('test111函数运行完毕')
test111() #这时候就不是运行单独test111()函数了,而是加上装饰器之后的wrapper
结果:
test111函数运行完毕
运行时间是3.0118308067321777
函数闭包加上返回值
加到wrapper中
def timer(fun):
def wrapper():
star_time=time.time()
res=fun()
stop_time=time.time()
print('运行时间是%s' %(stop_time-star_time))
return res
return wrapper
@timer
def test111():
time.sleep(3)
print('test111函数运行完毕')
return '123'
#test111=timer(test111)
res=test111()
print('test111加上返回值:%s' %res)
结果:
test111函数运行完毕
运行时间是3.000683069229126
test111加上返回值:123
函数闭包加上参数
被修饰的函数有参数时
把参数加到wrapper上,然后值会传给被修饰对象
def timer(fun):
def wrapper(*args,**kwargs): #这样就不用担心参数个数变化了,更灵活 ,可变长参数 *args将参数变成元组,**kwargs将参数变成字典
star_time=time.time()
res=fun(*args,**kwargs) #上边传过来的
stop_time=time.time()
print('运行时间是%s' %(stop_time-star_time))
return res
return wrapper
@timer
def test111(name,age,gender):
time.sleep(3)
print('test111函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender))
return '123'
res=test111('letty',22,'femal')
print('test111加上返回值:%s' %res)
结果:
test111函数运行完毕,名字是【letty】 年龄是【22】 性别【femal】
运行时间是3.000082015991211
test111加上返回值:123
为函数加上认证功能
后端就是一个个功能,功能用函数实现
#利用装饰器给函数加上认证功能
def auth_fun(fun):
def wrapper(*args,**kwargs):
username=input('用户名:').strip()
passwd=input('密码:').strip()
if username=='clt' and passwd=='123456':
res=fun(*args,**kwargs)
return res
else:
print('用户名或者密码错误!')
return wrapper
@auth_fun
def index():
print('欢迎来到网上商城')
@auth_fun
def home(name):
print('欢迎回家 %s' %name)
index()
home('clt')
结果:
用户名:clt
密码:123456
欢迎来到网上商城
用户名:clt
密码:123456
欢迎回家 clt
装饰器模拟session
记录登录状态 ,使用全局变量
user_dic={'username':None,'login':False}
def auth_fun(fun):
def wrapper(*args,**kwargs):
if user_dic['username'] and user_dic['login']:
res=fun(*args,**kwargs)
return res
username=input('用户名:').strip()
passwd=input('密码:').strip()
if username=='clt' and passwd=='123456':
user_dic['username']=username
user_dic['login']=True
res=fun(*args,**kwargs)
return res
else:
print('用户名或者密码错误!')
return wrapper
@auth_fun
def index():
print('欢迎来到网上商城')
@auth_fun
def home(name):
print('欢迎回家 %s' %name)
index()
home('clt')
结果:只输一次用户名和密码就好了
用户名:clt
密码:123456
欢迎来到网上商城
欢迎回家 clt
上边的代码有个弊病:就是将用户名和密码写死了,下边完善
user_list=[
{'name':'alex','passwd':'123456'},
{'name':'clt','passwd':'123456'},
{'name':'peiqi','passwd':'123456'},
{'name':'qiaozhi','passwd':'123456'},
]
current_dic={'username':None,'login':False}
def auth_fun(fun):
def wrapper(*args,**kwargs):
if current_dic['username'] and current_dic['login']:
res=fun(*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=fun(*args,**kwargs)
return res
else:
print('用户名或者密码错误!')
return wrapper
@auth_fun
def index():
print('欢迎来到网上商城')
@auth_fun
def home(name):
print('欢迎回家 %s' %name)
print('befor-->',current_dic)
index()
print('after-->',current_dic)
home('clt')
结果:
befor--> {'username': None, 'login': False}
用户名:clt
密码:123456
欢迎来到网上商城
after--> {'username': 'clt', 'login': True}
欢迎回家 clt
#上边做用户认证,用户信息简单的来源于列表,而现实中可能有多种形式的来源方式,比如文件和数据库,并且数据库也有多种,比如关系型和非关系型。还有,每种函数的数据来源也可能不同,下边介绍带参数的装饰器
- 带参数装饰器
让用户选择认证类型
如何给写好的装饰器加参数?
用闭包(前边只用了高阶函数和函数嵌套)
在原来的装饰器之外再加一层,最外边一层传到的参数,可以传进里边的所有层
然后改变调用方式
user_list=[
{'name':'alex','passwd':'123456'},
{'name':'clt','passwd':'123456'},
{'name':'peiqi','passwd':'123456'},
{'name':'qiaozhi','passwd':'123456'},
]
current_dic={'username':None,'login':False}
def auth(auth_type='fieldb'):
def auth_fun(fun):
def wrapper(*args,**kwargs):
print('认证类型是%s' %auth_type)
if auth_type=='field':
if current_dic['username'] and current_dic['login']:
res=fun(*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=fun(*args,**kwargs)
return res
else:
print('用户名或者密码错误!')
elif auth_type=='idap':
print('这个玩法厉害了!!')
res=fun(*args,**kwargs)
return res
else:
print('我不会玩!你自己玩吧!')
return wrapper
return auth_fun
@auth(auth_type='field') #因为加括号了,所以这里直接运行,得到的是auth_fun的内存地址。实际上就等于 @auth_fun,但是附加了参数auth_type
def index():
print('欢迎来到网上商城')
@auth(auth_type='idap')
def home(name):
print('欢迎回家 %s' %name)
print('befor-->',current_dic)
index()
print('after-->',current_dic)
home('clt')
结果:
befor--> {'username': None, 'login': False}
认证类型是field
用户名:clt
密码:123456
欢迎来到网上商城
after--> {'username': 'clt', 'login': True}
认证类型是idap
这个玩法厉害了!!
欢迎回家 clt