python閉包和裝飾器

一、python函數(shù)作用域LEGB

python解釋器查找變量的原則(順序):
L→E→G→B
L:Local函數(shù)內(nèi)部作用域
E:enclosing函數(shù)內(nèi)部與內(nèi)嵌函數(shù)之間
G:gobal全局作用域
B:build-in內(nèi)置作用域

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2))
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

上面示例代碼中max函數(shù)為python的內(nèi)建函數(shù),在in_func函數(shù)中墙基,max這個(gè)變量python解釋器首先會(huì)在in_func這個(gè)函數(shù)中查找寂纪,即local函數(shù)內(nèi)部作用域中查找胆建,然后按順序再到E-G-B查找甥啄,最后在build-in內(nèi)置作用域查找到活尊。

二灰粮、閉包

1仔涩、概念:內(nèi)部函數(shù)中對(duì)enclosing作用域的變量進(jìn)行引用

上述例子中my_func函數(shù)中的內(nèi)部函數(shù)in_func引用了enclosing作用域中的value2變量,這就叫做閉包粘舟。
由于函數(shù)執(zhí)行完后變量會(huì)被回收熔脂,當(dāng)執(zhí)行完my_func這個(gè)函數(shù)后,value2變量會(huì)被回收柑肴。那么如果我們?cè)俅握{(diào)用in_func這個(gè)函數(shù)時(shí)霞揉,需用引用到value2這個(gè)變量,是否會(huì)報(bào)錯(cuò)晰骑?
看以下代碼:

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2)) #打印value2的ID值
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

f = my_func()    #my_func返回的是in_func适秩,此時(shí)f指向in_func函數(shù)
f()    #f()相當(dāng)于in_func(),調(diào)用了infunc函數(shù)
print(f.__closure__)

ouput:

501351024
6
(<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)

如果內(nèi)部函數(shù)引用了enclosing作用域的變量硕舆,會(huì)將變量添加到函數(shù)__closure__的屬性中去秽荞。當(dāng)再次查找這個(gè)變量時(shí),會(huì)直接去函數(shù)__closure__的屬性中查找抚官。我們可以看到代碼的輸出結(jié)果第一行即為value2的內(nèi)存地址(501351024轉(zhuǎn)換為16進(jìn)制:1DE20270)和__closure__屬性中的int對(duì)象的地址是一樣的( int object at 0x000000001DE20270)扬跋。

2、那么閉包到底有什么用呢凌节?

我們來(lái)看下以下兩段代碼:

(1)胁住、

def func_100(value):
    passline = 60
    if value >= passline:
        print('pass')
    else:
        print('failed')

def func_150(value):
    passline = 90
    if value >= passline:
        print('pass')
    else:
        print('failed')
func100(59)
func150(89)

(2)趁猴、

def set_passline(passline):
    def in_func(value):
        if value >= passline:
            print('pass')
        else:
            print('failed')
    return in_func
f_100 = set_passline(60) #passline=60被存儲(chǔ)在f_100的__closure__屬性中
f_150 = set_passline(90) #passline=90被存儲(chǔ)在f_150的__closure__屬性中
f_100(59)
f_150(89)

兩段代碼都能正確判斷滿(mǎn)分是100或150時(shí),分?jǐn)?shù)是否及格彪见。但是第二段代碼儡司,由于運(yùn)用閉包,代碼復(fù)用性更高余指。

3捕犬、閉包更高級(jí)的應(yīng)用

將閉包的概念中的變量變成函數(shù),同樣適用酵镜。即:內(nèi)部函數(shù)中對(duì)enclosing作用域的函數(shù)進(jìn)行引
example:

def my_sum(*args):
    return(sum(args))

def my_average(*args):
    return sum(args)/len(args)

def dec(func):
    def in_dec(*args):
        if len(args) == 0:    #對(duì)參數(shù)進(jìn)行判斷碉碉,如果沒(méi)有參數(shù)直接返回0
            return 0
        for i in args:
            if not isinstance(i, int):    #對(duì)參數(shù)進(jìn)行判斷,如果有一個(gè)參數(shù)不是int類(lèi)型淮韭,直接返回0
                return 0
        return func(*args)    #此處對(duì)enclosing作用域的func函數(shù)進(jìn)行引用
    return in_dec

evo_my_sum = dec(my_sum)
evo_my_average = dec(my_average)

PS:此處求和函數(shù)(my_sum)和求平均值函數(shù)(my_average)只對(duì)參數(shù)是否int類(lèi)型進(jìn)行判斷垢粮。
我們來(lái)分析下代碼:evo_my_sum = dec(my_sum)
由于函數(shù)dec返回的是in_dec函數(shù)
所以evo_my_sum指向的是in_dec函數(shù)
=》evo_my_sum = in_dec
=》evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)
那么in_dec(1, 2, 3)evo_my_sum(1, 2, 3)會(huì)對(duì)求和的參數(shù)先進(jìn)行判斷后,再調(diào)用my_sum函數(shù)靠粪。
同樣道理:
evo_my_average會(huì)對(duì)求平均的參數(shù)先進(jìn)行判斷后蜡吧,再調(diào)用my_average函數(shù)。
這樣就可以對(duì)參數(shù)統(tǒng)一進(jìn)行判斷后再各自調(diào)用不同的函數(shù)占键。

三昔善、裝飾器

裝飾器是用來(lái)裝飾函數(shù)的,它返回一個(gè)函數(shù)對(duì)象畔乙。語(yǔ)法:@Decorator
現(xiàn)在我們已經(jīng)定義了一個(gè)my_sum函數(shù)

def my_sum(*args):
    return(sum(args))

假設(shè)我們要增加my_sum函數(shù)的功能君仆,比如,在函數(shù)調(diào)用前對(duì)參數(shù)進(jìn)行一個(gè)判斷牲距,但又不希望修改my_sum函數(shù)的定義返咱,這種在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式,稱(chēng)之為“裝飾器”(Decorator)牍鞠。
我們要定義一個(gè)能判斷參數(shù)的decorator洛姑,如下:

def dec(func):
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

按照python裝飾器的語(yǔ)法:

@dec
def my_sum(*args):
    return(sum(args))

這樣,調(diào)用my_sum函數(shù)皮服,不僅會(huì)運(yùn)行my_sum函數(shù)本身,還會(huì)在運(yùn)行my_sum函數(shù)前参咙,對(duì)參數(shù)進(jìn)行判斷龄广。
把@dec放到my_sum函數(shù)的定義處,相當(dāng)于執(zhí)行了語(yǔ)句:
my_sum = dec(my_sum)
看到這里是否覺(jué)得有點(diǎn)熟悉蕴侧?
其實(shí)這個(gè)語(yǔ)句和上述閉包的高級(jí)應(yīng)用是一樣的择同,只不過(guò)那部分把my_sum改成了evo_my_sum
裝飾器到這里還差最后一步:
my_sum函數(shù)經(jīng)過(guò)裝飾后净宵,由于dec(my_sum)返回的是in_dec函數(shù)敲才,此時(shí)my_sum.__name__屬性將從‘my_sum’變成‘in_dec’裹纳,為了保證此屬性不變,需在in_dec函數(shù)定義前加上語(yǔ)句@functools.wraps(func)紧武,保證my_sum.__name__屬性不變剃氧。否則,有些依賴(lài)函數(shù)簽名的代碼執(zhí)行就會(huì)出錯(cuò)阻星。一個(gè)完整的decorator的寫(xiě)法如下:

import functools

def dec(func):
    @functools.wraps(func)
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

decorator可以增強(qiáng)函數(shù)的功能朋鞍,定義起來(lái)雖然有點(diǎn)復(fù)雜,但使用起來(lái)非常靈活和方便妥箕。
以上是觀看慕課網(wǎng)《python裝飾器》以及廖雪峰教程python裝飾器的總結(jié)滥酥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市畦幢,隨后出現(xiàn)的幾起案子坎吻,更是在濱河造成了極大的恐慌,老刑警劉巖宇葱,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘦真,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡贝搁,警方通過(guò)查閱死者的電腦和手機(jī)吗氏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雷逆,“玉大人弦讽,你說(shuō)我怎么就攤上這事“蛘埽” “怎么了往产?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)某宪。 經(jīng)常有香客問(wèn)我仿村,道長(zhǎng),這世上最難降的妖魔是什么兴喂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任蔼囊,我火速辦了婚禮,結(jié)果婚禮上衣迷,老公的妹妹穿的比我還像新娘畏鼓。我一直安慰自己,他們只是感情好壶谒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布云矫。 她就那樣靜靜地躺著,像睡著了一般汗菜。 火紅的嫁衣襯著肌膚如雪让禀。 梳的紋絲不亂的頭發(fā)上挑社,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音巡揍,去河邊找鬼痛阻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吼肥,可吹牛的內(nèi)容都是我干的录平。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缀皱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼斗这!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起啤斗,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤表箭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后钮莲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體免钻,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年崔拥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了极舔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡链瓦,死狀恐怖拆魏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情慈俯,我是刑警寧澤渤刃,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站贴膘,受9級(jí)特大地震影響卖子,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刑峡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一洋闽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧突梦,春花似錦诫舅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)铐伴。三九已至撮奏,卻和暖如春俏讹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畜吊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工泽疆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玲献。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓殉疼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捌年。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓢娜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 閉包: 在函數(shù)內(nèi)部在定義一個(gè)函數(shù),并且這個(gè)函數(shù)用到了外面函數(shù)的變量,這個(gè)函數(shù)和用到的變量,稱(chēng)為閉包. deftes...
    界面大叔閱讀 219評(píng)論 0 0
  • 前幾天學(xué)習(xí)python裝飾器時(shí),看各種例子礼预,上來(lái)就是一個(gè)嵌套函數(shù)眠砾,還返回一個(gè)函數(shù)對(duì)象(返回內(nèi)嵌函數(shù)),學(xué)得我是一臉...
    喵小琪閱讀 352評(píng)論 0 1
  • 本文為《爬著學(xué)Python》系列第四篇文章托酸。從本篇開(kāi)始褒颈,本專(zhuān)欄在順序更新的基礎(chǔ)上,會(huì)有不規(guī)則的更新励堡。 在Pytho...
    SyPy閱讀 2,501評(píng)論 4 11
  • 曾經(jīng)谷丸,在酷暑的夏天,寒冷的冬季应结,每天早上出門(mén)等公交車(chē)的時(shí)候我就會(huì)想要是有輛自行車(chē)就好了刨疼。這樣我就可以騎車(chē)在家和地鐵...
    葉子綠漫天閱讀 204評(píng)論 0 0