一、闭包函数
闭包函数=函数嵌套定义+函数对象+名称空间与作用域
1、闭:指的是该函数是定义在一个函数内部的函数
2、包:值得是该函数访问了一个来自于外层函数的变量
为函数体传参的方法:
'''方案一:直接使用参数的形式传递'''
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
'''方案二:把函数体想要的参数包给它(即使用闭包的概念)'''
def outter(x):
x = 111
def wrapper(): # wrapper = 闭包函数的内存地址
print(x)
return wrapper # 一定不要加括号
return 闭包函数的内存地址
f1 = outter(111) # f = 闭包函数的内存地址
f2 = outter(222) # f = 闭包函数的内存地址
乍一看会感觉使用闭包来传参数非常的麻烦,我们之前使用函数,需要参数都是直接传给他,方便也快捷,但是在某些场景下我们定死了某个函数无法直接传参数,那就必须通过其他方式传参数,即闭包的方式,下面介绍的装饰器就是闭包的使用
二、无参装饰器
1、什么是装饰器
器:工具
装饰:为被装饰者添加额外的功能
2、为何要有装饰器
软件一旦上线运行之后,就应该遵循开放封闭原则:
1、开放指的是对拓展新功能开放
2、封闭指的是对修改源代码封闭
这种情况下,我们在写新功能时,若需要用到新的参数,就无法直接通过原函数来传了,必须通过其他方式来传参,目前我们想到的方法,是两种传参的另一种的方式,闭包函数
定义装饰器的目的:
定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的
ps:
不修改被装饰对象指的是定义与调用都不能修改
所以下述行为都违反了开放封闭原则:
①、修改被装饰对象定义时的源代码
②、修改被装饰对象的调用方式
3、如何用装饰器:
'''无参装饰器基本格式'''
def inner(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return func
return wrapper()
4、无参装饰器的应用场景以及构建装饰器步骤
需求:有一个index函数,此时我们要在index函数基础上添加一个计算运算时间的功能,因为是公司项目,我们不能修改原函数的调用方式,也不能修改源代码。
一、被装饰器对象index如下
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
二、为index添加计算运算时间功能
方案1问题:修改了源代码
import time
def index(x,y):
start = time.time()
time.sleep(1)
print("index---->",x,y)
end = time.time()
print(end-start)
index(1,2)
方案2问题:需要找到所有调用index的位置
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
start = time.time()
index(1,2)
end = time.time()
print(end-start)
方案3问题:函数被写死了只能算index的时间
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def wrapper():
start = time.time()
index(1,2)
end = time.time()
print(end-start)
wrapper()
方案4:直接传index进去,改变了index的调用方式,现在需要用wrapper来调用index,我们需要再想一种传参的方式
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def wrapper(func):
start = time.time()
func(1,2)
end = time.time()
print(end-start)
wrapper(index)
方案5问题:此时利用闭包函数,将index包给了wrapper,再重命名为index,我们在调用index时其实是调用wrapper,但有出现了问题,\
原函数在调用的时候需要传参数如index(1,2),而wrapper接收不了参数
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def outter(func):
# func = index()
def wrapper():
start = time.time()
func(1,2)
end = time.time()
print(end-start)
return wrapper
index = outter(index)
index()
方案6
import time
from functools import wraps
def timmer(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(end-start)
return res
return wrapper
def login(func):
def wrapper(*args,**kwargs):
name = 'yang'
pwd = '123'
inp_name = input("请输入您的用户名:").strip()
inp_pwd = input("请输入您的密码").strip()
if inp_name == name and inp_pwd == pwd:
print("登录成功")
res = func(*args,**kwargs)
return res
return wrapper
@timmer
@login
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
三、有参装饰器
对于不再需要新参数的装饰器,两层就可以解决了,但是当我们的装饰器需要新的参数,如在登录的时候,我们要判断我们用户名与密码的来源,此时需要外部将来源传进来。而两层的装饰器中,第一层,是为了给原函数传参数,他的参数不能动,而第二层,因为装饰器语法@的原因,也已经定死了此处只能传一个函数名,也不能传参数,所以我们需要构造第三层来接受外部的参数,而在内部的函数可以在不改动自己依然可以获取到外层的函数.
'''有参装饰器模版'''
def outter2(x,y,z,a,b):
def outter1(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter1
def outter(engine = 'file'):
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if engine == "file":
print('基于file的认证')
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
elif engine == 'mysql':
print('基于mysql的认证')
elif engine == 'ldap':
print('基于ldap的认证')
else:
print('未知的engine')
return wrapper2
return deco2
@outter(engine='mysql') # @deco2 # index=deco2(index)
def index(x,y):
print('index=>',x,y)
index(1,2) # index=>wrapper
四、叠加多个装饰器的运行步骤(记住结论既可)
2.1 加载顺序:自下而上
2.2 执行顺序:自上而下运行内层的wrapper函数
结论:
1、无参装饰器的模板
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
2、叠加多个装饰器
**2.1 加载顺序:自下而上**
**2.2 执行顺序:自上而下运行内层的wrapper函数**
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('wrapper1====>')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('wrapper2====>')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址
def wrapper3(*args,**kwargs):
print('wrapper3====>')
res3=func3(*args,**kwargs)
return res3
return wrapper3
# index=wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址
@deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址
def index(x,y):
print('index=>',x,y)
index(1,2)
"""
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
"""
3 案例
import time
def deco1(func1):
def wrapper1(*args,**kwargs):
start_time = time.time()
res1=func1(*args,**kwargs)
stop_time = time.time()
print(stop_time - start_time)
return res1
return wrapper1
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
return wrapper2
@deco2
@deco1
def index(x,y):
time.sleep(1)
print('index=>',x,y)
index(1,2)