python闭包与装饰器
本文针对: 学不会,学过即忘,学完跑路 的相关python人群.
一.Python闭包
定义: 访问了定义体以外的定义的非全局变量.(刚学时看不懂这句话太TM正常了)
定义解析: 其实就是函数里面再定义一个函数,里层函数引用了外层函数的变量,这就是闭包.
1 def wrapper(): 2 whatever = 'abandon' 3 def inner(): 4 print(whatever) 5 print(inner.__closure__) # 闭包: (<cell at 0x000002584285E558: str object at 0x000002584445F230>,) 6 7 wrapper()
这里需要说明的是闭包形成与否和是否返回内存函数并无直接关系,只是这样我们就无法运用闭包了,所以正常情况下必须返回.
二.Python装饰器
1.这玩意有什么用
装饰器,装饰器,自然是装饰东西用的.这里的"东西"即是python中的一等公民--函数.
2.函数为什么需要装饰
这个说起来话就长了,这里长话短说,总结成一句话: 中心思想: 简单省事以及--别动老子代码.
3.装饰器的作用
这里引入一个软件设计的原则:
开放封闭原则--对扩展代码的功能是开放的,但是对修改源代码是封闭的.(即功能你可以加,但是代码您可别动...)
那么python中怎么去实现呢--装饰器啊!
4.装饰器的写法和引用
a.装饰器需要用到闭包;
b.装饰器需要返回内存函数;
c.装饰器使用"@"符号调用;
ex:
1 def zsq(func): # zsq: decorator的拼音 2 def inner(): 3 print('卧槽!') # 加一个热血沸腾的语气词 4 func() 5 return inner 6 7 @zsq # 调用装饰器 8 def haoyougei(): 9 print("你不要过来啊!") 10 11 @zsq # 调用装饰器 12 def aduogeng(): 13 print("为什么是你!") 14 15 @zsq # 调用装饰器 16 def axiba(): 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 return inner 6 7 @zsq # 调用装饰器 8 def haoyougei(name): 9 print("你不要过来啊!{}".format(name)) 10 11 @zsq # 调用装饰器 12 def aduogeng(name): 13 print("为什么是你!{}".format(name)) 14 15 @zsq # 调用装饰器 16 def axiba(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 return inner 10 return zsq # 返回装饰器,zsq才是真正的装饰器,decorator是其包装 11 12 @decorator() # 调用装饰器decorator ***** 调用的是最外层的包装 13 def haoyougei(name): 14 print("你不要过来啊!{}".format(name)) 15 16 @decorator(flag=False) # 调用装饰器decorator ***** 调用的是最外层的包装 17 def aduogeng(name): 18 print("为什么是你!{}".format(name)) 19 20 @decorator() # 调用装饰器decorator ***** 调用的是最外层的包装 21 def axiba(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!

浙公网安备 33010602011771号