闭包函数
闭包函数=名称空间与作用域+函数嵌套+函数对象(综合应用)
闭包函数:保障数据安全
# 只存在嵌套函数中
# 内层函数使用外层非全局变量的引用
def func():
a=[]
def func1():
a.append(1)
return func1
这样a变量与函数func1绑定,在内存中不会消失
像a这种变量叫做自由变量
装饰器
定义:在不改变原函数的代码以及调用方式的前提下,为被装饰对象增添新的功能
开放封闭原则:开放是指对代码的拓展是开放的,封闭是指对源码的修改是封闭的
推导方式和语法糖:
# 原函数
def index(x,y)
return x+y
import time
# 现在想要添加一个功能,能够测量函数执行的时间
# 推导过程一:
def wrapper():
start=time.time()
index(111,222)
stop=time.time()
print(stop - start)
wrapper()
# 这样虽然功能实现了,但是传参数的时候有问题,如果传递的参数太多无法执行
# 二:解决参数问题
import time
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def wrapper(*args,**kwargs):
start=time.time()
index(*args,**kwargs) # index(3333,z=5555,y=44444)
stop=time.time()
print(stop - start)
wrapper(3333,4444,5555)
wrapper(3333,z=5555,y=44444)
# 三:因为装饰器应该可以装饰不同的函数,最好的解决办法就是将被装饰的函数当做参数传进去
import time
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
# func = index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs) # index的内存地址()
stop=time.time()
print(stop - start)
index=outter(index) # index=wrapper的内存地址
home=outter(home) # home=wrapper的内存地址
home('egon') # 虽然这个home并不是函数home本身(两个home的id并不同),但是对于用户来说是一样的
# 四:解决原函数的返回值问题
import time
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper # 原函数返回给res,res返回给wrapper,wrapper再返回给outter
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
home=outter(home)
res=home('libai') # 这样就和原来的调用方式一样了
##### 语法糖:方便装饰器的使用 #####
import time
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper
# 在被装饰对象正上方的单独一行写@装饰器名字
@timmer # 就相当于执行了index=timmer(index),为什么叫语法糖,就是单纯觉得长得像而已
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
@timmer # home=timmer(ome)
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
index(x=1,y=2,z=3)
home('egon')
##### 总结无参装饰器模板 #####
def outter(func):
def wrapper(*args,**kwargs):
# 在函数执行之前为其添加的新的操作
res=func(*args,**kwargs)
# 在函数执行之后为其添加的新的操作
return res
return wrapper
# 装饰器就是将原函数名指向的内存地址偷梁换柱成wrapper函数,所以应该将wrapper做的跟原函数一样才行,这里的一样应该是除了函数名的内存地址,其他的包括功能、传参、以及内置的属性都应该一样的才对,上面解决了传参和功能,下面说一下属性的问题(就是内置的__name__、__doc__等方法)
from functools import wraps
def outter(func):
@wraps(func)
# 该模块的装饰器wraps能够将函数的内置属性传给装饰器
# 1. 函数wrapper.__name__ = 原函数.__name__
# 2. 函数wrapper.__doc__ = 原函数.__doc__
# wrapper.__name__ = func.__name__
# wrapper.__doc__ = func.__doc__
def wrapper(*args, **kwargs):
"""这里面是主要功能"""
res = func(*args, **kwargs) # res=index(1,2)
return res
return wrapper
##### 下面解决一下装饰器传参的方法,装饰器有的时候可能根据传参的不同,实现不同的功能选择,如根据数据来源的不同选择不同的验证方式
# 推导过程介绍:装饰器的功能是在wrapper函数加上的,因为最后执行的是wrapper函数
def auth(func,db_type):
def wrapper(*args, **kwargs):
name=input('your name>>>: ').strip()
pwd=input('your password>>>: ').strip()
if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')
return wrapper
@auth # 账号密码的来源是文件
def index(x,y):
print('index->>%s:%s' %(x,y))
@auth # 账号密码的来源是数据库
def home(name):
print('home->>%s' %name)
@auth # 账号密码的来源是ldap
def transfer():
print('transfer')
# 要实现上述的功能,就要多传进去一个参数,下面的方法是可以实现的
index=auth(index,'file')
home=auth(home,'mysql')
transfer=auth(transfer,'ldap')
# 但是这样是无法使用语法糖的,因为语法糖@deco只实现了index=auth(index),没有传进去参数
# 推导二:想法就是多写一层,通过返回值传递的方式将auth(index,'file')的值传给deco(index)
def auth(db_type):
def deco(func):
def wrapper(*args, **kwargs):
name=input('your name>>>: ').strip()
pwd=input('your password>>>: ').strip()
if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')
return wrapper
return deco
deco=auth(db_type='file')
@deco # 账号密码的来源是文件
def index(x,y):
print('index->>%s:%s' %(x,y))
deco=auth(db_type='mysql')
@deco # 账号密码的来源是数据库
def home(name):
print('home->>%s' %name)
deco=auth(db_type='ldap')
@deco # 账号密码的来源是ldap
def transfer():
print('transfer')
index(1,2)
home('egon')
transfer()
# 这里,装饰器是没问题了,但是装饰器的使用出现问题了,我们想要@deco,但是函数实际上是auth,所以这里就是将auth(db_type='ldap')的值传给deco,进而实现deco的功能
# 正式版本:
def auth(db_type):
def deco(func):
def wrapper(*args, **kwargs):
name = input('your name>>>: ').strip()
pwd = input('your password>>>: ').strip()
if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs) # index(1,2)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')
return wrapper
return deco
@auth(db_type='file') # 实际上就是@deco,只是将(db_type='file')的值传给了deco,从而实现语法糖传参
def index(x, y):
print('index->>%s:%s' % (x, y))
@auth(db_type='mysql') # @deco # home=deco(home) # home=wrapper
def home(name):
print('home->>%s' % name)
@auth(db_type='ldap') # 账号密码的来源是ldap
def transfer():
print('transfer')
index(1, 2)
home('egon')
transfer()
##### 有参装饰器模板:比起无参装饰器就是多套了一层 #####
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 deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('正在运行===>deco1.wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('正在运行===>deco2.wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(x):
def outter3(func3): # func3=被装饰对象index函数的内存地址
def wrapper3(*args,**kwargs):
print('正在运行===>deco3.outter3.wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
return outter3
##### 加载的顺序(就是代码没执行的时候)是自下而上#####
@deco1 # index=deco1(wrapper2的内存地址) ===> index=wrapper1的内存地址
@deco2 # index=deco2(wrapper3的内存地址) ===> index=wrapper2的内存地址
@deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
def index(x,y):
print('from index %s:%s' %(x,y))
##### 执行代码的顺序是自上而下的 #####
# 装饰器实际上也是一个函数
# 执行代码的时候,deco1就是装饰的是deco2,指向的是wrapper2的内存地址,deco2装饰的是deco3,指向的是wrapper3的内存地址,deco3(本质上是outter)装饰的是被装饰函数index,就这样一个一个的传递下来,执行所有的代码