簡(jiǎn)簡(jiǎn)單單說個(gè)閉包

閉包的作用

一句話大莫,閉包的作用:將方法存于變量蛉腌。

至于閉包的原因或者目的,或者說只厘,為什么將方法存于變量烙丛,稍后再說。

閉包的條件

為了盡量避免用一大段話描述一個(gè)概念羔味,我們理性一點(diǎn)地把閉包的條件劃分成3個(gè):

  1. 外函數(shù)中定義了一個(gè)內(nèi)函數(shù)
  2. 內(nèi)函數(shù)用了外函數(shù)的變量
  3. 外函數(shù)返回了內(nèi)函數(shù)的引用河咽,or,外函數(shù)中直接調(diào)用了內(nèi)函數(shù)

P.S.

  1. 其中外函數(shù)和內(nèi)函數(shù)是指嵌套函數(shù)中外部函數(shù)和內(nèi)部函數(shù)
  2. 也正是因?yàn)樾枰短缀瘮?shù)赋元,因此不支持的嵌套函數(shù)的語言也自然不支持此類閉包
  3. 條件3中分成了兩類忘蟹,更多的情況下是前一類,而后一類(外函數(shù)直接調(diào)用了內(nèi)函數(shù))的使用更多的是為了保證代碼的簡(jiǎn)潔搁凸,而如此地保持簡(jiǎn)潔并不一定用閉包媚值。

閉包的例子

“Talk is cheap, show me your code.”

我始終覺得,在編程中护糖,過多的人類語言會(huì)產(chǎn)生太多的歧義褥芒,甚至還可能會(huì)因?yàn)樗f事物過于抽象而導(dǎo)致聽眾無法將概念理解。

而解決這個(gè)問題最好的方法就是看代碼嫡良,編程語言相較于人類語言的優(yōu)點(diǎn)之一是大幅地降低了語言的歧義锰扶。同時(shí),通過多個(gè)代碼實(shí)例皆刺,人腦會(huì)自然而然地將多實(shí)例中的共同點(diǎn)提取出來少辣,進(jìn)而理解抽象的概念凌摄。

結(jié)合閉包的三個(gè)條件羡蛾,我們來看看閉包的例子:

def outer(b):
    def inner(a):  # 條件1
        print(a + b)  # 條件2
    return inner  # 條件3
    
# 調(diào)用
o = outer(1)
o(2)

Python的代碼還是挺簡(jiǎn)單的。

image

一般情況下锨亏,在函數(shù)結(jié)束后痴怨,函數(shù)中變量等就應(yīng)該被銷毀忙干,偏偏這個(gè)閉包就是個(gè)特例 —— o和o2中的1和20都保留著。

o和o2看起來就有那么一絲熟悉的感覺浪藻,它們兩個(gè)就像是兩個(gè)對(duì)象 —— 這兩個(gè)“對(duì)象”都是從同一個(gè)“類”出來的捐迫,而兩個(gè)“對(duì)象實(shí)例”的區(qū)別是有一個(gè)加數(shù)不一樣,分別是1和20(當(dāng)然爱葵,這兩個(gè)變量的引用地址也不同)施戴。

現(xiàn)在,我們把代碼例子中的第三個(gè)條件變一下萌丈,即將“外函數(shù)返回了內(nèi)函數(shù)的引用”變成“外函數(shù)中直接調(diào)用了內(nèi)函數(shù)”:

def outer(a, b):
    def inner(a):  # 條件1
        print(a + b)  # 條件2
    inner(b)  # 條件3
    
# 調(diào)用
o = outer(100赞哗,1)
image

此時(shí),整個(gè)閉包函數(shù)調(diào)用起來就和一個(gè)普通函數(shù)一樣辆雾,傳入兩個(gè)參數(shù)肪笋,該print的也如期而至。

只能說度迂,在outer函數(shù)內(nèi)的邏輯過于復(fù)雜的時(shí)候藤乙,inner能把復(fù)雜的代碼“模塊化”,再調(diào)用惭墓,能增加簡(jiǎn)潔性坛梁。這種情況下,一般是inner函數(shù)只被調(diào)用一次诅妹,而且只在這里調(diào)用罚勾,放在這里也好管理一些。

接下來吭狡,我們也鑒賞一下別的語言類似的閉包:

func outer(i int) func() int {
    return func() int {  // 條件1(匿名)+ 條件3
        i++  // 條件2
        return i
    }
}

// 調(diào)用
o := outer(1)
o()

這個(gè)Golang的例子閉包和Python的例子較大的距別是這里還把內(nèi)函數(shù)換成了匿名的尖殃,看起來會(huì)爽點(diǎn)。而以下的php的就和Python的差不多了划煮。

function outer($str1) {
    $outerStr = $str1;
    $inner = function($str2) {  // 條件1
        echo $str2 . $outerStr;  // 條件2
    };
    return $inner;  // 條件3
}

// 調(diào)用
$o = outer("hahaha");
$o("emmm");

閉包的原因

看過了上面的例子后送丰,新手對(duì)閉包的概念也應(yīng)該有了一定的理解,甚至有點(diǎn)想法了弛秋。

回到最開始的問題器躏,即閉包的原因。

如果說蟹略,“將方法存于變量”是閉包的目的登失,那么接下來的問題顯而易見:為什么要將方法存于變量?直接調(diào)用方法(函數(shù))不好嗎挖炬?

閉包保存了函數(shù)的狀態(tài)信息

再舉開頭的例子:

def outer(b):
    def inner(a):
        print(a + b)
    return inner
    
o = outer(1)
o(2)  # 3
o(100)  # 101
o2 = outer(20)
o2(100)  # 120

o這個(gè)變量對(duì)應(yīng)的閉包保存了b=1這個(gè)信息揽浙,之后無論是調(diào)用o(2)還是o(100)b=1這個(gè)信息依然會(huì)存在并和后來的參數(shù)一起參與運(yùn)算。同理馅巷,o2這個(gè)變量對(duì)應(yīng)的閉包保存了b=20這個(gè)信息膛虫。

由于退出了函數(shù)后,函數(shù)并沒有并銷毀钓猬,這個(gè)閉包的信息也沒銷毀稍刀,因此后續(xù)可以利用這些信息。

語法糖

為了代碼的簡(jiǎn)潔性和易理解性敞曹,我們經(jīng)常會(huì)使用甚至創(chuàng)造一些語法糖账月。

而在Python中,有一個(gè)十分好看的例子就是裝飾器澳迫,舉個(gè)已經(jīng)被用爛了的例子捶障,面向切面的登錄實(shí)現(xiàn):

# 先實(shí)現(xiàn)一個(gè)類似于裝飾器的函數(shù)
def decorator(func): 
    def inner():
        print 'before function'
        func()  # function
        print 'after function'
    return inner

# 實(shí)現(xiàn)一個(gè)假裝在登錄的登錄函數(shù)
def login():  
    print 'login function complete.'

# 將登錄函數(shù)“套上”裝飾器
login = decorator(login)  
login()   

整個(gè)過程下來與AOP類似,而場(chǎng)景也很常用纲刀,如統(tǒng)計(jì)函數(shù)的運(yùn)行時(shí)常项炼、加入日志、統(tǒng)一的過濾處理等等示绊。

更有甚者锭部,在特定的場(chǎng)景下使用閉包創(chuàng)造語法糖,以簡(jiǎn)化代碼面褐,參考這個(gè)例子拌禾,該例子可以替代switch(不過這里這么簡(jiǎn)單的加減運(yùn)算這樣寫就很智障了,要有一定的復(fù)雜度就能顯得有優(yōu)越性):

def operator(o):
    def plus(x, y):
        print(x + y)
    def minus(x, y):
        print(x - y)
        
    if o == '+':
        return plus
    if o == '-':
        return minus

def f(x, o, y):
    operator(o)(x, y)

函數(shù)式編程

鼎鼎大名的Lambda展哭,這個(gè)可以參考下這個(gè)鏈接湃窍。

總結(jié)

閉包能將方法存于變量,且實(shí)現(xiàn)一些美妙的東西匪傍。

它就像是調(diào)味劑您市,并非不可或缺,但是能錦上添花役衡。

先這樣吧

若有錯(cuò)誤之處請(qǐng)指出茵休,更多地關(guān)注煎魚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末手蝎,一起剝皮案震驚了整個(gè)濱河市榕莺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棵介,老刑警劉巖钉鸯,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異邮辽,居然都是意外死亡唠雕,警方通過查閱死者的電腦和手機(jī)扣蜻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來及塘,“玉大人,你說我怎么就攤上這事锐极◇狭牛” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵灵再,是天一觀的道長(zhǎng)肋层。 經(jīng)常有香客問我,道長(zhǎng)翎迁,這世上最難降的妖魔是什么栋猖? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮汪榔,結(jié)果婚禮上蒲拉,老公的妹妹穿的比我還像新娘。我一直安慰自己痴腌,他們只是感情好雌团,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著士聪,像睡著了一般锦援。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剥悟,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天灵寺,我揣著相機(jī)與錄音,去河邊找鬼区岗。 笑死略板,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的慈缔。 我是一名探鬼主播蚯根,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼胀糜!你這毒婦竟也來了颅拦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤教藻,失蹤者是張志新(化名)和其女友劉穎距帅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體括堤,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碌秸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年绍移,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讥电。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹂窖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恩敌,到底是詐尸還是另有隱情瞬测,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布纠炮,位于F島的核電站月趟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恢口。R本人自食惡果不足惜孝宗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耕肩。 院中可真熱鬧因妇,春花似錦、人聲如沸猿诸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽两芳。三九已至摔寨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怖辆,已是汗流浹背是复。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竖螃,地道東北人淑廊。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像特咆,于是被迫代替她去往敵國(guó)和親季惩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line)腻格,也就是一...
    悟名先生閱讀 4,118評(píng)論 0 13
  • 閉包是自包含的函數(shù)代碼塊画拾,可以在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代...
    莽原奔馬668閱讀 1,876評(píng)論 2 12
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,740評(píng)論 0 38
  • “也不知道這人什么來頭菜职,瞧他那傲慢樣兒青抛,背后的金主估計(jì)……得比喬二爺還高” 幾個(gè)衣著靚麗的女孩在角落里議論紛紛,眼...
    嚼嚼是我呢閱讀 192評(píng)論 0 0
  • 才智通靈思緒明举瑰, 慧泉蠢動(dòng)涌入心捣辆。 惠情拂面了人心, 暢所欲言快我情此迅。
    青殊文衫閱讀 152評(píng)論 0 0