闭包与装饰器
| 閉包與裝飾器 |
一、閉包
閉包是語(yǔ)法閉包的簡(jiǎn)稱(chēng),是引用自由變量的函數(shù)。這個(gè)被引用的自由變量將與這個(gè)函數(shù)一同存在,即使已經(jīng)離開(kāi)創(chuàng)造它的環(huán)境也不例外。所以,閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。閉包是函數(shù)式編程的重要語(yǔ)法結(jié)構(gòu)。Python也支持這一特性。
假設(shè)我們通過(guò)配置實(shí)現(xiàn)打招呼的函數(shù)實(shí)現(xiàn)閉包:
def GreetingConfig(prefix):def greeting(postfix):print(prefix,postfix)return greeting res = GreetingConfig('Good morning!') res('alex')在以上程序中,GreetingConfig()函數(shù)嵌套greeting()函數(shù),且greeting()函數(shù)作為返回值。greeting()函數(shù)訪問(wèn)上一級(jí)函數(shù)的變量prefix。此時(shí),GreetingConfig()函數(shù)就是閉包。
閉包的總結(jié):
- 閉包函數(shù)必須有嵌套函數(shù);
- 嵌套函數(shù)需要引用上一級(jí)函數(shù)的變量;
- 閉包函數(shù)必須返回嵌套函數(shù)。
二、裝飾器
裝飾器本質(zhì)上就是函數(shù),功能是為其他函數(shù)添加附加功能。
通俗的來(lái)講,裝飾器就是等于高階函數(shù)+函數(shù)嵌套+閉包的綜合實(shí)現(xiàn)。
裝飾器有兩大原則:第一,不修改被修飾函數(shù)的源代碼;第二,不修改被修飾函數(shù)的調(diào)用方法。
我們通過(guò)實(shí)例一步步講解裝飾器的功能,首先我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的延時(shí)函數(shù),并在程序中調(diào)用它,即:
import timedef test():time.sleep(3)print('test函數(shù)運(yùn)行完畢') test()
執(zhí)行以上程序的運(yùn)行結(jié)果為:
test函數(shù)運(yùn)行完畢函數(shù)功能非常簡(jiǎn)單:延時(shí)3秒鐘,延時(shí)后打印,然后在程序中調(diào)用該函數(shù)。
假設(shè)此時(shí)想添加一個(gè)新功能:計(jì)算出test()函數(shù)總共的運(yùn)行時(shí)間,為了使源代碼保持不變,在這里我們就可以用裝飾器來(lái)實(shí)現(xiàn),即:
import timedef timmer(func): #func=testdef wrapper():start_time = time.time()func() #就是在運(yùn)行test()stop_time = time.time()print('運(yùn)行時(shí)間是%s'%(stop_time-start_time))return wrapper@timmer #相當(dāng)于test=timmer(test) def test():time.sleep(3)print('test函數(shù)運(yùn)行完畢')test()
執(zhí)行以上程序的運(yùn)行結(jié)果為:
test函數(shù)運(yùn)行完畢 運(yùn)行時(shí)間是3.000999927520752在以上程序中,timmer()函數(shù)就是一個(gè)裝飾器,裝飾器完美地實(shí)現(xiàn)了它的兩大原則,既沒(méi)有修改源代碼,也沒(méi)用修改調(diào)用方法。其中@timmer中的@是Python裝飾器的語(yǔ)法糖。@timmer放在test()函數(shù)的定義處,相當(dāng)于執(zhí)行了語(yǔ)句:test=timmer(test),這就是調(diào)用方法為什么沒(méi)被修改的原因。
1.被裝飾的函數(shù)帶參數(shù)
在以上程序中,被裝飾的函數(shù)test()沒(méi)有參數(shù),如果有參數(shù),則裝飾器該如何構(gòu)建呢?基于以上程序,修改為如下代碼,即:
import time def timmer(func): #func=testdef wrapper(*args,**kwargs): start_time = time.time()func(*args,**kwargs) #就是在運(yùn)行test()stop_time = time.time()print('運(yùn)行時(shí)間是%s'%(stop_time-start_time))return wrapper@timmer #test=timmer(test) def test(name,age):time.sleep(3)print('test函數(shù)運(yùn)行完畢,名字是%s,年齡是%s'%(name,age)) test('alex',18)執(zhí)行以上程序的運(yùn)行結(jié)果為:
test函數(shù)運(yùn)行完畢,名字是alex,年齡是18 運(yùn)行時(shí)間是3.000999927520752可以看到,當(dāng)被裝飾的函數(shù)test()有參數(shù)時(shí),可將裝飾器的嵌套函數(shù)修改為被裝飾器函數(shù)的形式即可。
若想要使用共同的裝飾器來(lái)修飾多個(gè)不同的函數(shù),則這些不同的函數(shù)參數(shù)形式不同,裝飾器可以通過(guò)可變參數(shù)(*args,**kwargs)的方式來(lái)實(shí)現(xiàn)嵌套函數(shù)。
2.帶參數(shù)的裝飾器
對(duì)于裝飾器本身帶參數(shù)的情況我們應(yīng)該如何實(shí)現(xiàn),基于前面的例子,為裝飾器添加一個(gè)bool變量,可以通過(guò)變量的真假判斷是否調(diào)用計(jì)時(shí)功能,即:
import time def ctime(flag = False):if flag:def timmer(func): #func=testdef wrapper(*args,**kwargs):start_time = time.time()func(*args,**kwargs) #就是在運(yùn)行test()stop_time = time.time()print('運(yùn)行時(shí)間是%s'%(stop_time-start_time))return wrapperelse:def timmer(func):return funcreturn timmer@ctime(flag = False) #test=timmer(test) def test(name,age):time.sleep(3)print('test函數(shù)運(yùn)行完畢,名字是%s,年齡是%s'%(name,age)) test('alex',18)@ctime(flag = True) #test=timmer(test) def test1(name,age):time.sleep(3)print('test1函數(shù)運(yùn)行完畢,名字是%s,年齡是%s'%(name,age)) test1('lisi',20)執(zhí)行以上程序的運(yùn)行結(jié)果為:
test函數(shù)運(yùn)行完畢,名字是alex,年齡是18 test1函數(shù)運(yùn)行完畢,名字是lisi,年齡是20 運(yùn)行時(shí)間是3.000999927520752分析以上程序可以看出,將同一個(gè)裝飾器ctime用于兩個(gè)不同的函數(shù):test()和test1()。另外,通過(guò)裝飾器的參數(shù)可以為裝飾過(guò)程添加判斷,@ctime(True)表示進(jìn)行計(jì)時(shí),@ctime(False)不會(huì)進(jìn)行計(jì)時(shí),程序運(yùn)行結(jié)果也證明了這一點(diǎn)。
3.裝飾器調(diào)用順序
使用多個(gè)裝飾器修飾同個(gè)函數(shù)時(shí),裝飾器的調(diào)用順序與語(yǔ)法糖@的聲明順序相反。
轉(zhuǎn)載于:https://www.cnblogs.com/lzc69/p/11196085.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: Zookeeper实现注册与发现
- 下一篇: C# 判断txt文件编码格式