一句話,閉包的做用:將方法存於變量。php
至於閉包的緣由或者目的,或者說,爲何將方法存於變量,稍後再說。git
爲了儘可能避免用一大段話描述一個概念,咱們理性一點地把閉包的條件劃分紅3個:github
P.S.編程
「Talk is cheap, show me your code.」閉包
我始終以爲,在編程中,過多的人類語言會產生太多的歧義,甚至還可能會由於所說事物過於抽象而致使聽衆沒法將概念理解。編程語言
而解決這個問題最好的方法就是看代碼,編程語言相較於人類語言的優勢之一是大幅地下降了語言的歧義。同時,經過多個代碼實例,人腦會天然而然地將多實例中的共同點提取出來,進而理解抽象的概念。模塊化
結合閉包的三個條件,咱們來看看閉包的例子:函數式編程
def outer(b): def inner(a): # 條件1 print(a + b) # 條件2 return inner # 條件3 # 調用 o = outer(1) o(2)
Python的代碼仍是挺簡單的。函數
通常狀況下,在函數結束後,函數中變量等就應該被銷燬,恰恰這個閉包就是個特例 —— o和o2中的1和20都保留着。spa
o和o2看起來就有那麼一絲熟悉的感受,它們兩個就像是兩個對象 —— 這兩個「對象」都是從同一個「類」出來的,而兩個「對象實例」的區別是有一個加數不同,分別是1和20(固然,這兩個變量的引用地址也不一樣)。
如今,咱們把代碼例子中的第三個條件變一下,即將「外函數返回了內函數的引用」變成「外函數中直接調用了內函數」:
def outer(a, b): def inner(a): # 條件1 print(a + b) # 條件2 inner(b) # 條件3 # 調用 o = outer(100,1)
此時,整個閉包函數調用起來就和一個普通函數同樣,傳入兩個參數,該print的也如期而至。
只能說,在outer函數內的邏輯過於複雜的時候,inner能把複雜的代碼「模塊化」,再調用,能增長簡潔性。這種狀況下,通常是inner函數只被調用一次,並且只在這裏調用,放在這裏也好管理一些。
接下來,咱們也鑑賞一下別的語言相似的閉包:
func outer(i int) func() int { return func() int { // 條件1(匿名)+ 條件3 i++ // 條件2 return i } } // 調用 o := outer(1) o()
這個Golang的例子閉包和Python的例子較大的距別是這裏還把內函數換成了匿名的,看起來會爽點。而如下的php的就和Python的差很少了。
function outer($str1) { $outerStr = $str1; $inner = function($str2) { // 條件1 echo $str2 . $outerStr; // 條件2 }; return $inner; // 條件3 } // 調用 $o = outer("hahaha"); $o("emmm");
看過了上面的例子後,新手對閉包的概念也應該有了必定的理解,甚至有點想法了。
回到最開始的問題,即閉包的緣由。
若是說,「將方法存於變量」是閉包的目的,那麼接下來的問題顯而易見:爲何要將方法存於變量?直接調用方法(函數)很差嗎?
再舉開頭的例子:
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這個變量對應的閉包保存了b=1
這個信息,以後不管是調用o(2)
仍是o(100)
,b=1
這個信息依然會存在並和後來的參數一塊兒參與運算。同理,o2這個變量對應的閉包保存了b=20
這個信息。
因爲退出了函數後,函數並無並銷燬,這個閉包的信息也沒銷燬,所以後續能夠利用這些信息。
爲了代碼的簡潔性和易理解性,咱們常常會使用甚至創造一些語法糖。
而在Python中,有一個十分好看的例子就是裝飾器,舉個已經被用爛了的例子,面向切面的登陸實現:
# 先實現一個相似於裝飾器的函數 def decorator(func): def inner(): print 'before function' func() # function print 'after function' return inner # 實現一個僞裝在登陸的登陸函數 def login(): print 'login function complete.' # 將登陸函數「套上」裝飾器 login = decorator(login) login()
整個過程下來與AOP相似,而場景也很經常使用,如統計函數的運行時常、加入日誌、統一的過濾處理等等。
更有甚者,在特定的場景下使用閉包創造語法糖,以簡化代碼,參考這個例子,該例子能夠替代switch(不過這裏這麼簡單的加減運算這樣寫就很智障了,要有必定的複雜度就能顯得有優越性):
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)
鼎鼎大名的Lambda,這個能夠參考下這個連接。
閉包能將方法存於變量,且實現一些美妙的東西。
它就像是調味劑,並不是不可或缺,可是能錦上添花。
先這樣吧
如有錯誤之處請指出,更多地關注煎魚。