簡簡單單說個閉包

2021-08-20 10:26:01 字數 3291 閱讀 8555

一句話,閉包的作用:將方法存於變數。

至於閉包的原因或者目的,或者說,為什麼將方法存於變數,稍後再說。

為了盡量避免用一大段話描述乙個概念,我們理性一點地把閉包的條件劃分成3個:

外函式中定義了乙個內函式

內函式用了外函式的變數

外函式返回了內函式的引用,or,外函式中直接呼叫了內函式

p.s.

其中外函式和內函式是指巢狀函式中外部函式和內部函式

也正是因為需要巢狀函式,因此不支援的巢狀函式的語言也自然不支援此類閉包

條件3中分成了兩類,更多的情況下是前一類,而後一類(外函式直接呼叫了內函式)的使用更多的是為了保證**的簡潔,而如此地保持簡潔並不一定用閉包。

「talk is cheap, show me your code.」

我始終覺得,在程式設計中,過多的人類語言會產生太多的歧義,甚至還可能會因為所說事物過於抽象而導致聽眾無法將概念理解。

而解決這個問題最好的方法就是看**,程式語言相較於人類語言的優點之一是大幅地降低了語言的歧義。同時,通過多個**例項,人腦會自然而然地將多例項中的共同點提取出來,進而理解抽象的概念。

結合閉包的三個條件,我們來看看閉包的例子:

def

outer

(b):

definner

(a):

# 條件1

print(a + b) # 條件2

return inner # 條件3

# 呼叫

o = outer(1)

o(2)

python的**還是挺簡單的。

一般情況下,在函式結束後,函式中變數等就應該被銷毀,偏偏這個閉包就是個特例 —— o和o2中的1和20都保留著。

o和o2看起來就有那麼一絲熟悉的感覺,它們兩個就像是兩個物件 —— 這兩個「物件」都是從同乙個「類」出來的,而兩個「物件例項」的區別是有乙個加數不一樣,分別是1和20(當然,這兩個變數的引用位址也不同)。

現在,我們把**例子中的第三個條件變一下,即將「外函式返回了內函式的引用」變成「外函式中直接呼叫了內函式」:

def

outer

(a, b):

definner

(a):

# 條件1

print(a + b) # 條件2

inner(b) # 條件3

# 呼叫

o = outer(100,1)

此時,整個閉包函式呼叫起來就和乙個普通函式一樣,傳入兩個引數,該print的也如期而至。

只能說,在outer函式內的邏輯過於複雜的時候,inner能把複雜的**「模組化」,再呼叫,能增加簡潔性。這種情況下,一般是inner函式只被呼叫一次,而且只在這裡呼叫,放在這裡也好管理一些。

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

func outer(i int) func() int 

}// 呼叫

o := outer(1)

o()

這個golang的例子閉包和python的例子較大的距別是這裡還把內函式換成了匿名的,看起來會爽點。而以下的php的就和python的差不多了。

function

outer

($str1) ;

return

$inner; // 條件3

}// 呼叫

$o = outer("hahaha");

$o("emmm");

看過了上面的例子後,新手對閉包的概念也應該有了一定的理解,甚至有點想法了。

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

如果說,「將方法存於變數」是閉包的目的,那麼接下來的問題顯而易見:為什麼要將方法存於變數?直接呼叫方法(函式)不好嗎?

再舉開頭的例子:

def

outer

(b):

definner

(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中,有乙個十分好看的例子就是裝飾器,舉個已經被用爛了的例子,面向切面的登入實現:

# 先實現乙個類似於裝飾器的函式

defdecorator

(func):

definner

():print

'before function'

func() # function

print

'after function'

return inner

# 實現乙個假裝在登入的登入函式

deflogin

():print

'login function complete.'

# 將登入函式「套上」裝飾器

login = decorator(login)

login()

整個過程下來與aop類似,而場景也很常用,如統計函式的執行時常、加入日誌、統一的過濾處理等等。

def

operator

(o):

defplus

(x, y):

print(x + y)

defminus

(x, y):

print(x - y)

if o == '+':

return plus

if o == '-':

return minus

deff

(x, o, y):

operator(o)(x, y)

閉包能將方法存於變數,且實現一些美妙的東西。

它就像是調味劑,並非不可或缺,但是能錦上添花。

先這樣吧

簡簡單單說個閉包

一句話,閉包的作用 將方法存於變數。至於閉包的原因或者目的,或者說,為什麼將方法存於變數,稍後再說。為了盡量避免用一大段話描述乙個概念,我們理性一點地把閉包的條件劃分成3個 外函式中定義了乙個內函式 內函式用了外函式的變數 外函式返回了內函式的引用,or,外函式中直接呼叫了內函式 p.s.其中外函式...

簡簡單單編譯原理

都說編譯原理挺難,其實它並不像大家想象中的那麼難,it s so easy 總的來說編譯原理可以分為以下幾個知識點 文法的分類 不確定的有限狀態自動機到確定的有限狀態自動機的轉換 正規表示式。掌握了這三個知識點,編譯原理中每個題的道理了,其實編譯原理就是這麼回事。下面具體介紹上面說的三個知識點 1....

簡簡單單寫程式

寫程式這麼多年,總有幾百萬行了。感覺,程式寫的方向,總的來說越來越簡單。乙個函式,簡簡單單幾行就完了,乙個類,簡簡單單幾個公有方法,也就完了。簡單,就不容易出錯,自己看自己的 看昏了的也有,乙個函式幾千行,人的腦子,堆疊顯然不夠用,看了後面,忘了前面,何苦呢。簡單,就好懂,乙個工程完了,心裡其實很發...