闭包函数
闭包函数=函数嵌套定义+函数对象+名称空间与作用域
核心点:名字的查找关系是以函数定义阶段为准
闭包函数定义
1、闭:指的是该函数是定义在一个函数内部的函数
2、包:指的是该函数访问了一个来自于外层函数的变量
为函数体传参的方式
方案一:直接使用参数的形式传递
def wrapper(x):
print(x)
wrapper(111)
方案二:把函数体想要的参数包给他(闭包)
def outter():
x = 111
def wrapper(): # wrapper = 闭包函数的内存地址
print(x)
return wrapper # 一定不要加()
# return 闭包函数的内存地址
# wrapper()
f=outter()
f() # 111
print(f) # <function outter.<locals>.wrapper at 0x7fa98f6cd790>
wrapper = outter() # wrapper = 闭包函数的内存地址
f1 = outter(111) # f = 闭包函数的内存地址
f2 = outter(222) # f = 闭包函数的内存地址
装饰器
什么是装饰器
器 : 工具
装饰:为被装饰者添加额外的功能
合到一起的解释:装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
为何要有装饰器
软件一旦上线运行之后,就应该遵循开放封闭原则:
1、开放指的是对拓展新功能开放
2、封闭指的是对修改源代码封闭
定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的
ps:
不修改被装饰对象指的是定义与调用都不能修改
所以下述行为都违反了开放封闭原则:
1、修改被装饰对象定义时的源代码
2、修改被装饰对象的调用方式
无参装饰器
无参装饰器的模板
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
无参装饰器的演变
需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
import time
def index(x,y):
time.sleep(3)
print('index %s %s' %(x,y))
index(111,222)
# index(y=111,x=222)
# index(111,y=222)
演变一:直接修改源代码为被装饰对象index添加新功能
问题:没有修改被装饰对象的调用方式,但是修改了其源代码
import time
def index(x,y):
start_time = time.time() # time.time()主要用来计算时间间隔
time.sleep(1)
print('index===>',x,y)
stop_time = time.time()
print('run time is: %s'%(stop_time-start_time))
index(1,2)
演变二:找到所有调用index的位置,然添加代码
问题:需要写好多段重复代码
没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能,但是代码冗余
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
start_time = time.time()
index(1,2)
stop_time = time.time()
print('run time is: %s'%(stop_time-start_time))
start_time = time.time()
index(1,2)
stop_time = time.time()
print('run time is: %s'%(stop_time-start_time))
# 没运行一次都需要重新敲一遍代码 ,需要写好多段重复代码
演变三:把装饰功能写到一个函数内
问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了,调用方式变成了wrapper()
装饰功能被写死了,只能用来装饰index函数了
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def wrapper():
start_time = time.time()
index(1,2)
stop_time = time.time()
print('run time is: %s'%(stop_time-start_time))
wrapper()
wrapper()
wrapper()
演变四:wrapper函数内的index修改为参数的形式
问题:直接为wrapper函数传参的方案不行,因为原函数的调用是在全局调用index(1,2), 而现在的调用统一都变成了wrapper(index),这修改了被装饰的调用方式
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def wrapper(func):
start_time = time.time()
func(1,2)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
wrapper(index)
演变二:五:基于闭包函数 把wrapper函数想要的参数包给他
基于闭包函数 把wrapper函数想要的参数包给他,然后基于函数对象把闭包函数wrapper返回到全局,赋值给原函数名
把index伪装成了wrapper,但伪装的不够像:参数的传递与原函数不一致, 传递内容固定,函数内设置了(1,2)就只能传(1,2),不能想传什么就传什么
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def outter(func):
# func = index
def wrapper():
start_time = time.time()
func(1,2)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
return wrapper
# f=outter()
# f() # func = index 的时候用
index=outter(index) # 前面的index跟后面的index不是同一个,前面的是自己设置的可以随意改
index()
演变六:将index的参数写活了,可以传值
问题:只能装饰index
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def outter(func):
# func = index
def wrapper(x,y):
start_time = time.time()
func(x,y)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
return wrapper
# f=outter()
# f() # func = index 的时候用
index=outter(index)
index(1,2)
演变七:在六的基础上基于*args, **kwarg设置装饰对象
在六的基础上基于*args,**kwargs把wrapper的参数伪装成跟被装饰函数一模一样。
在方案六的基础上把被装饰对象写活了,原来只能装饰index,现在可以自己设置装饰对象
问题:返回值与原函数不一致
import time
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def home(name):
time.sleep(0.5)
print('welcome %s to home'%name)
return 123
def outter(func):
# func = index
def wrapper(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
return wrapper
# f=outter()
# f() # func = index 的时候用
index=outter(index)
home=outter(home)
# index(1,2)
# home=outter(home)
index(1,2)
# home('egon')
home(name='sa')
res=home('egon') # res=wrapper('egon')
print('返回值--》',res) # 返回None
演变八:在七的基础上把wrapper函数的返回值与被装饰函数保持一致
问题:装饰器的使用不够简洁
import time
from functools import wraps
def timmer(func):
# func = index
@wraps(func)
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
return res # 多了res 上面让res=func(*args,**kwargs)
return wrapper
def index(x,y):
time.sleep(1)
print('index===>',x,y)
def home(name):
"home的文档注释"
time.sleep(0.5)
print('welcome %s to home page' %name)
return 123
index=timmer(index)
home=timmer(home)
res=index(1,2)
print(res) # 返回 None
res=home('egon')
print(res) # 返回 123
# print(home.__name__)
# print(home.__doc__)
help(home)
# 没有@wraps(func)
# Help on function wrapper in module __main__:
# wrapper(*args, **kwargs)
# # func = index
# # @wraps(func)
# 有@wraps(func)
# Help on function home in module __main__:
# home(name)
# home的文档注释
演变九:语法糖,让代码更简便
import time
def timmer(func):
# func = index
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))
return res # 多了res 上面让res=func(*args,**kwargs)
return wrapper
@timmer # index=timmer(index)
def index(x,y):
time.sleep(1)
print('index===>',x,y)
@timmer # home=timmer(home)
def home(name):
"home的文档注释"
time.sleep(0.5)
print('welcome %s to home page' %name)
return 123
res=index(1,2)
print(res) # 返回 None # 因为wrapper的return res ,res指的是index,
# index函数 return返回值为None
res=home('egon')
print(res) # 返回 123 # 因为wrapper的return res ,res指的是home(),
# home()函数 return返回值为123
叠加多个装饰器
@deco1
@deco2
@deco3
加载顺序:自下而上
执行顺序:自上而下运行内层的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
"""
# 案例
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
# 先运行deco1 再运行deco2
# 登录的时候就开始deco1计时 登陆成功运行index(),运行完结束计时
@deco1
@deco2
def index(x,y):
time.sleep(1)
print('index=>',x,y)
index(1,2)
# 先运行deco2 再运行deco1
# 登录成功后开始deco1计时,运行完index()结束计时
@deco2
@deco1
def index(x,y):
time.sleep(1)
print('index=>',x,y)
index(1,2)
偷梁换柱:from functools import wraps,@wraps(func)
即将原函数名指向的内存地址偷梁换柱成wrapper函数,所以应该将wrapper做的跟原函数一样才行
修改前的内容
def outter(func):
def wrapper(*args, **kwargs):
"""这个是主页功能2"""
res = func(*args, **kwargs) # res=index(1,2)
return res
# 手动将原函数的属性赋值给wrapper函数
# 1、函数wrapper.__name__ = 原函数.__name__
# 2、函数wrapper.__doc__ = 原函数.__doc__
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
# 因为__xx__属性很多不可能手动将所有属性赋值给wrapper 所以用了个from functools import wraps 导入别人的模块
return wrapper
@outter # index=outter(index)
def index(x,y):
"""这个是主页功能1"""
print(x,y)
# index(1,2) # wrapper(1,2)
# 1、函数wrapper.__name__ = 原函数.__name__
# 2、函数wrapper.__doc__ = 原函数.__doc__
# index.__name__ = ' index'
# index.__doc__ = 原函数.__doc__
# 将函数放到wrapper里让他真正调用
print(index.__name__)
# print(help(index))
print(index.__doc__)
from functools import wraps
def outter(func):
@wraps(func) # 会自动帮你获取func的一系列属性并赋值给wrapper
def wrapper(*args, **kwargs):
"""这个是主页功能2"""
res = func(*args, **kwargs) # res=index(1,2)
return res
# 手动将原函数的属性赋值给wrapper函数
# 1、函数wrapper.__name__ = 原函数.__name__
# 2、函数wrapper.__doc__ = 原函数.__doc__
# wrapper.__name__ = func.__name__
# wrapper.__doc__ = func.__doc__
# 因为__xx__属性很多不可能手动将所有属性赋值给wrapper 所以用了个from functools import wraps 导入别人的模块
return wrapper
@outter # index=outter(index)
def index(x,y):
"""这个是主页功能1"""
print(x,y)
# print(help(index)) # 查看函数的帮助信息 可以查看函数的注释信息
print(index.__name__)
print(index.__doc__) #help(index)
index(1,2) # wrapper(1,2)
有参装饰器
有参装饰器模板
def 有参装饰器(x,y,z):
def outter(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
return outter
@有参装饰器(1,y=2,z=3)
def 被装饰对象():
pass
有参装饰器应用
def outter(engine):
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的认证')
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args)
return res2
else:
print('username or password error')
elif engine == 'ldap':
print('基于ldap的认证')
else:
print('未知的engine')
return wrapper2
return deco2
@outter(engine='file') # @deco2 # index=deco2(index)
def index(x,y):
print('index=>',x,y)
@outter(engine='mysql') # @deco # index=deco(index) # index=wrapper
def home(name):
print('home->>%s' %name)
@outter(engine='ldap') # @deco # home=deco(home) # home=wrapper
def transfer():
print('transfer')
index(1,2) # index=>wrapper
home('sa')
transfer()