闭包&LEGB法则:
所谓闭包,就是将组成函数的语句和这些语句的执行环境打包一起时得到的对象
闭包最重要的价值在于封装上下文环境
下面有个列子来解释下闭包
列:
def funX(x): print('-----------开始------------') def funY(y): return x*y print('-----------结束-------------') return funY x=funX(4) print(x(5))
》》》-----------开始------------ 》》》-----------结束------------- 》》》20
我们来分析下代码,函数funY封装在funX中,我们先看这段代码x=funX(4),这段代码会如何执行呢。
首先,调用funX函数,然后将4传入进去,这里就是x指向4,是一个引用,这点要记住,后面装饰器重点讲的就是引用。随后就是进入funX函数了,执行第一个输出语句,编译器读到def funY():时,知道这里是一个函数的定义,所以编辑器会将这个函数读到内存中,但不会执行函数里面的语句,因为还没有变量来调用这个函数,然后编译器执行到第二个输出语句,所以输出结果时先输出这这两句输出代码,然后编译器读取到return funY时,程序结束,因为有return,所以一定要有变量来接收这个返回的值,我们仔细看一下,返回的值是一个函数,所以我们将这个函数的引用赋给x,所以x和funY是等价的,可以将x函数看做是funY函数,所以我们使用x就是在使用funY函数,funY函数需要一个参数,然后执行print(x(5)),因为funY函数里面返回x*y,所以就看到我们输出的结果了,没错,闭包就是这么简单。
接下来讲装饰器,什么叫装饰器呢,简单来说就是将程序里面可有可无或者不是核心代码的代码剥离出来,精简代码,使代码更具可读性,装饰器可以做很多事,不会装饰器的程序员就不叫程序员,这是一个很重要的知识点,它不同于继承,但又类似继承。下面我们用一段代码来讲解装饰器
def funX(f): print('-----------开始111111------------') def funY(): f() print('函数内111111。。。。') print('-----------结束111111-------------') return funY def fx(f): print('-----------开始222222------------') def fy(): f() print('函数内222222。。。。') print('-----------结束222222-------------') return fy @funX @fx def test(): print('test......') test()》》》-----------开始222222------------
》》》-----------结束222222-------------
》》》-----------开始111111------------
》》》-----------结束111111-------------
》》》test......
》》》函数内222222。。。。
》》》函数内111111。。。。
首先先看代码,先不看函数定义那部分,先看@funX @fx def test(): print('test......')这部分,我们按到test函数上面有两个语句,分别是@funX和@fx,这就是装饰器的结构,这样也叫做 语法糖,将它绑定test函数,然后我们看代码,这两个语法糖的作用是将test函数传入这两个语法糖中,这里有两个语法糖,我们先执行那个呢,我们可以将它看做是衣服,test函数就相当于内衣,而fx语法糖就相当于保暖衣,funX语法糖则相当于外套,我们穿衣服是先穿内衣再穿保暖衣再穿外套吧,这里就和穿衣服一样。
前面所有的语句都是定义函数,只有最后一句是调用函数,我们调用test函数,然后代码就会跳到
@funX @fx def test(): print('test......')这句,我们按穿衣服的顺序来看,这句代码就相当于funX(fx(test())),相当于函数的一层层嵌套,也就是刚才所说的穿衣服一样,1.将test()函数穿入fx函数中,这时fx函数中的f指向test(),这里就是上面闭包哪里讲的函数的引用,然后执行两句输出语句,闭包上面讲过,fx()函数里面还没有变量来调用fy()函数,所以编译器只是会将它读取一遍而不会执行它,这里函数会先输出:》》》-----------开始222222------------ 》》》-----------结束222222-------------然后程序执行到return fy时,这里的fy是fx里面定义的那个fy函数,这些代码就是fx(test())执行后返回了fy()这个函数。这里返回函数时后面不能加括号,因为这是一个函数的引用,这里返回的fy会赋值给另一个函数。
2.所以funX(fx(test()))就变成了funX(fy),这时,将fy函数传入funX函数中,funX里面的f函数就指向了fy,这里和上面的情况一样,不会执行funY函数,只是会将return前面不属于funY函数里面的代码执行一遍,所以这里会输出:
》》》-----------开始111111------------ 》》》-----------结束111111-------------程序读取到return funY 时,跳出函数,然后我们这句funX(fy)就变成funY了,这句就变成了执行funY函数,然后我们来看funY函数:def funY(): f() print('函数内111111。。。。')3.这里是第三步了,我们执行funY函数,首先执行f(),这里的f()就是一开始指向的fy函数,所以f()就是执行fy()函数,有可能有人这里有些不懂,这里为什么f()为什么指向的是fy()函数呢,我们可以反向推导,f()是由哪个函数引用过来的呢,这里我就不仔细多讲,我们执行fy()函数,这段代码是:def fy(): f() print('函数内222222。。。。')这里进入fy()函数,执行f()函数,因为这里的f函数是由test传过来的,所以这里的f()函数时test()函数,所以会输出:print('test......')然后执行后面那句print(‘函数内222222。。。。’),所以会输出:》》》函数内222222。。。。
4.程序到这里就把funX中的f()这句运行完毕,然后执行下一句print('函数内111111。。。。')输出:》》》函数内111111。。。。其结果就是如此,这就是装饰器的执行步骤,一遍看不懂可以多看几遍,难点及时函数之间的引用和调用,搞清楚这一点就说明理解装饰器了。