闭包函数:
在编程语言中,闭包(也称为词法闭包或函数闭包)是在具有一流函数的语言中实现词法范围的名称绑定的一种技术。操作,一个闭包是一个记录存储功能加上一个环境:映射关联每个自由变量的函数(在本地变量使用,但是一个封闭范围中定义)的价值或存储位置的名字创建绑定时关闭。与普通函数不同,闭包允许函数通过访问捕获的变量。内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包。函数函数在定义阶段名字的查找顺序就已经固定死了不会因为函数调用位置的变化而改变。
我们来看看要实现函数闭包要满足什么条件(缺一不可):
1.必须嵌套函数。
2.内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3.外部函数必须返回内嵌函数——必须返回那个内部函数
闭包的作用:可以保持程序上一次运行后的状态然后继续执行。
1 # 直接传参 2 def index(name): 3 print(name) 4 5 6 # 闭包传参 7 def foo(): 8 num=1 9 def add(n): 10 nonlocal num 11 num += n 12 return num 13 return add 14 f=foo() 15 print(f(1)) #2 16 print(f(2)) #4
python装饰器:就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名的情况下,给被装饰对象添加新的功能 。函数装饰器有开发封闭原则;开放是对扩展开放,封闭是对修改封闭。函数装饰器必须遵守的原则:一是不改变被装饰对象的源代码,二是不改变装饰对象的调用方式。
装饰器的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原“”函数的函数。
一般而言,我们要想拓展原来函数代码,需要再给原来的函数添加新的功能,最直接的办法就是进入代码里面修改,但是实际工作中,有些时候核心代码并不可以直接去改,所以在不改动原代码的情况下,我们可以再定义一个函数,但是生效需要再次执行函数,原先的调用方式可能就会改变,可能会影响你全部的代码运行,而装饰器具有的两大特点就能很好的解决这个问题:
我们先定义一个最基础的函数:
1 def sum1(): 2 sum = 1 + 2 3 print(sum) 4 sum1() # 3
如果我们想查看这个函数执行用了多长时间可以加入import time语句查看
import time # 查看程序运行用了多久时间 def sum1(): start = time.clock() sum = 1+2 print(sum) end = time.clock() print("time used:",end - start) sum1() # 3 time used: 1.3777790024702245e-05
但是如果我们想想查看很多函数的运行时间需要一个函数一个函数的去添加,这样就会很麻烦,我们可以考虑重新定义一个函数timeit(名字可以自己随便定义),将sum1的引用传递给他,然后在timeit中调用sum1并进行计时,这样,我们就达到了不改动sum1定义的目的,而且,不论我们看了多少个函数,我们都不用去修改函数定义了!
1 import time # 查看程序运行用了多少时间 2 def sum1(): 3 sum = 1+ 2 4 print (sum) 5 def timeit(func): 6 start = time.clock() 7 func() 8 end =time.clock() 9 print("time used:", end - start) 10 timeit(sum1) # 3 time used: 1.333334518519572e-05
乍一看,我们写的没啥问题,可以运行!但是还是修改了一部分代码,把sum1() 改成了timeit(sum1)。这样的话,如果sum1在多处都被调用了,你就不得不去修改这很多处的代码。所以,我们就需要杨sum1()具有和timeit(sum1)一样的效果,于是将timeit赋值给sum1。可是timeit是有参数的,所以需要找个方法去统一参数,将timeit(sum1)的返回值(计算运行时间的函数)赋值给sum1。这个时候装饰器的牛批之处就体现出来了。
1 import time # 查看程序的运行时间 2 def sum1(): 3 sum = 1+ 2 4 print (sum) 5 def timeit(func): 6 def test(): 7 start = time.clock() 8 func() 9 end =time.clock() 10 print("time used:", end - start) 11 return test 12 sum1 = timeit(sum1) 13 sum1() # 3 time used: 1.644445906174139e-05
如上就是一个简单的函数装饰器,我们只需要在定义sum1以后调用sum1之前,加上sum1= timeit(sum1),就可以达到计时的目的,这也就是装饰器的概念:我们在没有改变函数的调用方式的情况下就给函数增加了新的功能。但是如果我们需要装饰的函数量比较多,那么语法的输入量就会增大,这里python就提供了语法糖:@来降低语句的输出。语法糖在书写的时候应该与被装饰对象紧紧挨着,两者之间不要有空格。
1 import time 2 def timeit(func): 3 def test(): 4 start = time.clock() 5 func() 6 end =time.clock() 7 print("time used:", end - start) 8 return test 9 10 @timeit # python的语法糖 @ 后面加上函数的名字。 11 def sum1(): 12 sum = 1+ 2 13 print (sum) 14 sum1() # 3 time used: 1.5111124543221817e-05
装饰器模板:
装饰器函数的本质是闭包函数
def wrapper(func): #装饰器函数,func为被装饰函数 def inner(*args,**kwargs): """被装饰函数前需要添加的内容""" ret=func(*args,**kwargs) #被装饰函数 """被装饰函数后需要添加的内容""" return ret return inner # *args :按照位置传值,多余的参数都给args,以元祖的形式存储 # **kwargs :按照关键字传值,多余的参数个kwargs,以字典的形式存储 # 使用方法 @wrapper # 函数装饰器的语法糖 def fun(): print("hi")
装饰器升级版
1.带参数装饰器
1 def out_wrapper(flag): 2 def wrapper(func): #装饰器函数,func为被装饰函数 3 def inner(*args,**kwargs): 4 """被装饰函数前需要添加的内容""" 5 ret=func(*args,**kwargs) #被装饰函数 6 """被装饰函数后需要添加的内容""" 7 return ret 8 return inner 9 return wrapper
2.多个装饰器的调用
1 def wrapper1(func): 2 def inner(*args,**ksargs): 3 print('wrapper1') 4 ret=func() 5 print('wrapper1') 6 return ret 7 return inner 8 9 10 def wrapper2(func): 11 def inner(*args,**ksargs): 12 print('wrapper2') 13 ret=func() 14 print('wrapper2') 15 return ret 16 return inner 17 18 def wrapper3(func): 19 def inner(*args,**ksargs): 20 print('wrapper3') 21 ret=func() 22 print('wrapper3') 23 return ret 24 return inner 25 26 @wrapper3 27 @wrapper2 28 @wrapper1 29 def fun(): 30 print('func1') 31 fun() 32 33 34 # 输出结果为: 35 # wrapper3 36 #wrapper2 37 # wrapper1 38 # func1 39 # wrapper1 40 # wrapper2 41 # wrapper3
多个装饰器,运行顺序是从距离被装饰函数最近的一个装饰器开始的,即从wrapper1,wrapper2,wrapper3,一层一层嵌套,但从打印的结果可知,在被装饰函数之前运行的是从外到内的顺序,之后运行的是从内到外的顺序,类似套娃的结构。