本文针对: 学不会,学过即忘,学完跑路 的相关python人群.
一.Python闭包
定义: 访问了定义体以外的定义的非全局变量.(刚学时看不懂这句话太TM正常了)
定义解析: 其实就是函数里面再定义一个函数,里层函数引用了外层函数的变量,这就是闭包.
1 defwrapper():2 whatever = 'abandon'
3 definner():4 print(whatever)5 print(inner.__closure__) #闭包: (,)
6
7 wrapper()
最简单闭包形成
这里需要说明的是闭包形成与否和是否返回内存函数并无直接关系,只是这样我们就无法运用闭包了,所以正常情况下必须返回.
二.Python装饰器
1.这玩意有什么用
装饰器,装饰器,自然是装饰东西用的.这里的"东西"即是python中的一等公民--函数.
2.函数为什么需要装饰
这个说起来话就长了,这里长话短说,总结成一句话: 中心思想: 简单省事以及--别动老子代码.
3.装饰器的作用
这里引入一个软件设计的原则:
开放封闭原则--对扩展代码的功能是开放的,但是对修改源代码是封闭的.(即功能你可以加,但是代码您可别动...)
那么python中怎么去实现呢--装饰器啊!
4.装饰器的写法和引用
a.装饰器需要用到闭包;
b.装饰器需要返回内存函数;
c.装饰器使用"@"符号调用;
ex:
1 def zsq(func): #zsq: decorator的拼音
2 definner():3 print('卧槽!') #加一个热血沸腾的语气词
4 func()5 returninner6
7 @zsq #调用装饰器
8 defhaoyougei():9 print("你不要过来啊!")10
11 @zsq #调用装饰器
12 defaduogeng():13 print("为什么是你!")14
15 @zsq #调用装饰器
16 defaxiba():17 print("居然是你!")18
19 axiba()20 aduogeng()21 haoyougei()22
给函数加个文明的语气词
如上述代码,为了防止定义的函数被再次更改代码(以及简单方便),使用@调用装饰器是事半功倍的方法.
5.装饰器的特性:
a.能把被装饰的函数替换成其他函数;
所以上面代码可以理解为:
axiba = zsq(axiba)
axiba()
aduogeng = zsq(aduogeng)
aduogeng()
haoyougei = zsq(haoyougei)
haoyougei()
b.装饰器函数在加载模块上立即执行,不需要函数调用.(这一点请自行测试)
三.Python装饰器进阶
看了上面的部分后如果了解python函数的你在写代码时就可能发现下面的两个问题:
1.被装饰的函数有传参怎么办?
2.定义的装饰器想传参怎么办?
这里引用一下上面的例子:
① axiba = zsq(axiba)
② axiba()
①里面的执行完成后,axiba为zsq返回的inner,调用axiba()时执行的为inner(),此时内存中func保存为axiba,所以执行func()即为执行axiba();
若axiba有传参,如axiba(*args),则func中参数应该为*args,即func(*args),而func的传参需要从inner中传入,所以写为inner(*args).
所以由上得出, 被装饰的函数有传参时,应该设置在里层函数中,如下:
1 def zsq(func): #zsq: decorator的拼音
2 def inner(*args, **kwargs): #(*args, **kwargs)表示接受所有传参
3 print('卧槽!') #加一个热血沸腾的语气词
4 func(*args, **kwargs)5 returninner6
7 @zsq #调用装饰器
8 defhaoyougei(name):9 print("你不要过来啊!{}".format(name))10
11 @zsq #调用装饰器
12 defaduogeng(name):13 print("为什么是你!{}".format(name))14
15 @zsq #调用装饰器
16 defaxiba(name):17 print("居然是你!{}".format(name))18
19
20 #axiba = zsq(axiba)
21 axiba('张益达')22 #aduogeng = zsq(aduogeng)
23 aduogeng('律政先锋')24 #haoyougei = zsq(haoyougei)
25 haoyougei('斯内克')
被装饰的函数拥有传参
解决了一个问题,还剩一个问题,其实第二个问题算是功能性问题--为了实现一些功能而出现的,不然一个正常人不会想到给装饰器传什么参数.
怎么解决呢,其实实现起来非常简单,但是原理有一点绕: 想想上面第一个问题的参数是如何加进去的--最里层调用的函数(func)需要传参,则给其所在层的函数(inner)加上了传参.那么当装饰器函数(zsq)需要传参时,我们给其包装一层函数,并在包装函数上设置传参不就可以了.
所以,当定义的装饰器需要传参时,需要在外层再设置一层包装函数,并将包装函数设置好传参,最后将装饰器函数返回.
1 def decorator(flag=True): #给装饰器设置一个参数: 形成一个开关
2 def zsq(func): #zsq: decorator的拼音
3 def inner(*args, **kwargs): #(*args, **kwargs)表示接受所有传参
4 print('卧槽!') #加一个热血沸腾的语气词
5 if flag: #使用外部flag标签实现逻辑功能
6 func(*args, **kwargs)7 else:8 print('溜了溜了~')9 returninner10 return zsq #返回装饰器,zsq才是真正的装饰器,decorator是其包装
11
12 @decorator() #调用装饰器decorator ***** 调用的是最外层的包装
13 defhaoyougei(name):14 print("你不要过来啊!{}".format(name))15
16 @decorator(flag=False) #调用装饰器decorator ***** 调用的是最外层的包装
17 defaduogeng(name):18 print("为什么是你!{}".format(name))19
20 @decorator() #调用装饰器decorator ***** 调用的是最外层的包装
21 defaxiba(name):22 print("居然是你!{}".format(name))23
24
25 #axiba = zsq(axiba)
26 axiba('张益达')27 #aduogeng = zsq(aduogeng)
28 aduogeng('律政先锋')29 #haoyougei = zsq(haoyougei)
30 haoyougei('斯内克')
装饰器的传参实现
到这里基本上算是将python装饰器的基本概念和实现说明完毕,另提一嘴from functools import wraps,想多了解的话google一下吧.
自己写4到5个装饰器,基本上就初步掌握了.
The End!