python----------闭包 、装饰器
闭包:
就是内层函数对外层函数(非全局变量的)非全局变量的引用
def func(): name = '老人家' def func1(): print(name)#局部变量对全局变量的引用 因为并没有在这个函数总定义name只是在它的父级定义了 return func1 func()()
闭包函数:
内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数
#函数内部定义的函数称为内部函数
为什么使用闭包:
闭包的使用就是开辟一个不是立刻关闭的空间
因为我们的函数每当执行完就会关闭这个函数 ,但是如果我们的是个嵌套函数,我们执行完父级的过程了 但是子函数中的内容还没有执行,这个时候该怎么办呢? 这个时候就可以使用闭包让父级函数不能立刻关闭
进而可以让我们马上使用子函数中的内容
def func():#定义一个父级函数 name='eva' def inner():#在这个父级函数中再定义一个子函数 #这个子函数从它的开始到它的结束就是整个闭包 print(name) #这个是引用父级函数内的变量name #使用闭包就是防止父级函数使用完之后不能使用子函数了 所以我们就定义一个闭包 然后让父级函数不能立刻关闭 进而调用子函数 # return inner #为了让我们能使用子函数就把子函数的名字给返还出去 f= func() f()
闭包的整个过程就是在内存中开辟一个空间这个空间并不是父级函数执行完之后就会立刻关闭的,一般的都是函数执行是开辟一个空间然后执行完之后就关闭 闭包个并不是这个样 这个是在内存中,开辟一个不是立刻关闭的空间然后当父级执行完 又可以让子函数在这个空间内执行,然后执行完等许久不用了 python的回收机制就会把这个内存给回收了
检测闭包:
我们也可以检测这个函数是不是属于闭包
判断闭包函数的方法__closure__
name = 'alex' def func(): def Aveo(): print(name)#这时候这个name引用的不是父级的变量了是全局变量了 所以就不是闭包了 Aveo() print(Aveo.__closure__) #这个必须是这个写在子函数的结束位置 才能测试 func()
def fun(name):
#其实这里省略了一部name = 'hhh' #送一子函数还是引用了父函数的内容 那么就是闭包
def func1():
print(name)
func1()
print(func1.__closure__) #在子函数的结束检测子函数是不是闭包
fun('hhh')
闭包函数的嵌套:
def wrapper(): money =100 def func(): name= 'eva' def inner(): print(name,money) return inner return func f= wrapper()#这一步就是子函数f=func i =f() #i = inner i()
闭包的面试题:
name = '老南华' def wraaper(n): #这里相当于省略了一个n=name='老南华' def inner(): print(n) inner() print(inner.__closure__) wraaper(name)
函数的装饰器:
装饰器本质上是一个Python函数,它可以让其它函数在不做任何变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。他经常用于有切面需求的场景。比如:插入日志,性能测试,事务处理,缓存,权限校验等。有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。
其实函数的名字也是特殊的变量 所以函数名是可以进行传递的
# 函数名是函数的名字,本质:变量,特殊的变量。
# 函数名() 执行此函数。
什么是装饰器:
在不改变原函数的执行的情况下,为原函数增加额外的功能
import time #因为是测试代码的运行时间的 所以一就需要导入时间模块 def func1(): print('你想干什么 还想测试我') # 我们来做一个测试一个函数的功能就是测试这个函数的效率的 def timmer(f): def inner():#在嵌套一个函数 start_time = time.time() #开始时间 f() time.sleep(0.3) #因为你做的是测试一个函数的运行效率但是代码在计算机中运行快的 几乎不可见,那么我们就需要加个让它延迟3秒的功能就是调用sleep然后延迟0.3秒 end_time=time.time() #结束时间 print('此函数的执行效率%s'%(end_time-start_time)) return inner #把这个函数名返回给父级以便于调用 f = timmer(func1) f()
大家可以看到我们最后的赋值操作需要几步 那么如果我们再定义其他的修改的功能呢 ?就需要再定义新的功能再重新赋值吗?不 我们为了简化操作就定义了魔法糖
魔法糖的运用
import time def wraaper(f): def inner(): start_time = time.time() #开始时间 f() time.sleep(0.4) #为了防止此程序太快计算机看不到 我们就让它延迟0.3秒 end_time=time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner @ wraaper #大家看魔法糖的运用 就是在要测试的函数上定义一个@魔法符号 然后让它加上执行测试功能的函数的名称即可 def func(): print('就是你要测试我啊') func()
被装饰函数带参数的装饰器:
import time def timmer(f): # f = func1 函数名 def inner(*args,**kwargs): start_time = time.time() f(*args,**kwargs) time.sleep(0.3) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner @timmer # func1 = timmer(func1) # inner def func1(a,b): print(a,b) print('你有病呀,领导,测试我的执行效率干甚。') func1(1,2) #进行传递参数
格式:
def wrapper(func): def inner(*args,**kwargs): '''被装饰函数之前''' ret = func(*args,**kwargs) '''被装饰函数之后''' return ret return inner @wrapper def func(a,b): pass return 566 print(
func(*args,**kwargs)
)
如果你要查找你的函数中的名字和函数内的注释内容就可以用__name__ 和__doc__来表示
@wrapper def func(*args): print(666) return args print(func(*[1,2,3])) def wrapper(): pass def func1(): ''' 此函数是完成登陆功能,参数分别是...作用 :return: ''' print(6666) # print(func1.__name__) print(func1.__doc__) return True func1() print(func1.__name__) print(wrapper.__name__)
多个参数的装饰器
import time def timmer(*args,**kwargs): def wrapper(f): def inner(*args,**kwargs): if flag: start_time = time.time() ret = f(*args, **kwargs) time.sleep(0.3) end_time = time.time() print('此函数的执行效率%f' % (end_time - start_time)) else: ret = f(*args, **kwargs) return inner return wrapper flag = True @timmer(flag,2,3) # 两步:1,timmer(flag) --> wrapper 2,@wrapper 装饰器 def func1(): print(666) @timmer(flag) def func2(): print(333) func1()
func2()
带参数的装饰器的意思就好比 你的装饰器 其实就是来查看 100个不同函数的执行时间的 那么有一天 你突然不想查看了 那你难道还一一去修改吗 ,这个时候就凸显了带参数的装饰器的意义了,
你可以让装饰器含有参数 然后把装饰器的参数给修改掉就可以了 比如你在外面传递的是flag的所以那么你就只需要更改flag的值就可以了
带参数的装饰器的运行流程
多个装饰器修饰一个函数 主要是为多个一个函数赋予不同的功能
def wrapper1(func): def inner1(*args,**kwargs): print(111) func(*args,**kwargs) print(222) return inner1 def wrapper2(func): def inner2(*args,**kwargs): print(333) func(*args,**kwargs) print(4444) return inner2 @wrapper2 #里面的f==inner1 外面的f == inner2 @wrapper1 #里面的f==函数名f 外面的f == inner1 def func (): print('主函数') func()
保留被装饰过后的函数原信息:functools
import functools
因为被装饰过的后的函数的名字信息都成了装饰器内部的inner函数了 所以我们要要取被装饰过的函数的原信息就很难了,我们要取被装饰过后的函数的原名字什么的就需要用到functools
def wrapper(self, func): @functools.wraps(func) #让你传递进来的函数保留原名称和原信息 def inner(request,*args, **kwargs): self.request = request return func(request,*args, **kwargs)