Python函式式程式設計之閉包

2021-07-11 23:10:09 字數 2913 閱讀 1930

>>> 

def_div_fun

(n):

def_div_check_fun

(x):

return x % n > 0

return _div_check_fun

這是什麼?外層函式中巢狀了乙個函式,然後外層函式將內層函式作為返回值進行返回,同時,返回函式中的計算也擁有了外層函式的變數n,這種將外部函式的引數和自身的區域性變數儲存在函式中一起返回的結構就稱為「閉包」,它擁有極大的威力!

再看乙個例子

>>> 

deflazy_sum

(*lst):

defsum

(): ax = 0

for i in lst:

ax = ax + i

return ax

return sum

>>> f1 = lazy_sum(1,3,5,7)

>>> f2 = lazy_sum(1,3,5,7)

>>> f1 == f2

false

>>> f1()

16>>> f2()

16>>>

首先,因為不希望立即計算出求和的值,所以在內部再構造出乙個sum函式,通過返回sum函式來達到在需要時再呼叫計算求和的目的;

同時,返回的sum函式中,使用了外層函式的引數lst,也儲存著自身的區域性變數ax

其次,每一次呼叫lazy_sum,都會返回乙個新的函式,即使傳入了相同的引數;

最後,閉包返回了sum函式之後並沒有立即執行,而是等到呼叫sum()之後才執行函式。

講了這麼多,對閉包有了乙個大概的印象,知道了它就是乙個函式返回的函式物件之後,再來看看它正統的定義

在電腦科學中,閉包(closure)是詞法閉包(lexical closure)的簡稱,是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。閉包在執行時可以有多個例項,不同的引用環境和相同的函式組合可以產生不同的例項。
定義中的自由變數指的就是外層函式的引數,即使外層函式的作用域結束之後,引數仍然存在不會被**,因為它被閉包引用了!

基於上面的介紹,不知道讀者有沒有感覺這個東西和類有點相似,相似點在於他們都提供了對資料的封裝。不同的是閉包本身就是個方法。和類一樣,我們在程式設計時經常會把通用的東西抽象成類,(當然,還有對現實世界——業務的建模),以復用通用的功能。閉包也是一樣,當我們需要函式粒度的抽象時,閉包就是乙個很好的選擇。

在這點上閉包可以被理解為乙個唯讀的物件,你可以給他傳遞乙個屬性,但它只能提供給你乙個執行的介面。因此在程式中我們經常需要這樣的乙個函式物件——閉包,來幫我們完成乙個通用的功能,比如常用到的裝飾器。

閉包的使用很廣泛,譬如前面提到的高階函式均需要函式作為引數傳入,如若需要函式粒度的抽象上對函式進行更改,就可以使用閉包。

>>> 

defcount

(): fs =

for i in range(3):

deff():

return i * i

return fs

>>> f1, f2, f3 = count()

上面這段**,是不是感覺一切ok?但結果卻是這樣的:

>>> f1()

4>>> f2()

4>>> f3()

4

這是什麼bug!

分析一下,返回的f1,f2,f3呼叫時,用到了自由變數i,很明顯,在呼叫的時刻,i已經迴圈完成變成了2,而並非儲存每乙個內部函式時相應的值。

重點就在於閉包不是立即執行的!所以閉包中不要引用迴圈變數,或者後期會發生變化的變數

那麼如果一定要引用變化的變數,應該怎麼改呢?總體思想是再建立乙個函式,用該函式的引數繫結迴圈變數當前的值,無論該迴圈變數後續如何更改,已繫結到函式引數的值不變

>>> 

defcount

(): fs =

for i in range(3):

deff(i):

defg

():return i * i

return g

return fs

>>> f1, f2, f3 = count()

>>> f1()

0>>> f2()

1>>> f3()

4

關鍵在於立即執行,立即執行f(i),從而儲存變數的當前值。

閉包有效的減少了函式所需定義的引數數目。這對於並行運算來說有重要的意義。在並行運算的環境下,我們可以讓每台電腦負責乙個函式,然後將一台電腦的輸出和下一台電腦的輸入串聯起來。最終,我們像流水線一樣工作,從串聯的電腦集群一端輸入資料,從另一端輸出資料。這樣的情境最適合只有乙個引數輸入的函式。閉包就可以實現這一目的。

並行運算正成為乙個熱點。這也是函式式程式設計又熱起來的乙個重要原因。函式式程式設計早在2023年代就已經存在,但應用並不廣泛。然而,我們上面描述的流水線式的工作並行集群過程,正適合函式式程式設計。由於函式式程式設計這一天然優勢,越來越多的語言也開始加入對函式式程式設計正規化的支援。

總的來說,閉包的理解沒有那麼困難,靈活使用閉包,能夠發揮其強大的作用。

函式式程式設計 閉包

def curve pie a 25 defcurve x return a pow x,2 return curve f curve pie print f 2 輸出結果 ans 100 檢驗函式是否閉包 print f.closure 環境變數 a 25 print f.closure 0 ce...

C 函式式程式設計之用閉包封裝資料

如果乙個程式語言能夠用高階函式解決問題,則意味著資料作用域問題已十分突出。當函式可以當成引數和返回值在函式之間進行傳遞時,編譯器利用閉包擴充套件變數的作用域,以保證隨時能得到所需要的資料。c 函式式程式設計之作用域 在c 中,變數的作用域是嚴格確定的。其本質是所有 生存在類的方法中 所有變數只生存於...

C 函式式程式設計之用閉包封裝資料

如果乙個程式語言能夠用高階函式解決問題,則意味著資料作用域問題已十分突出。當函式可以當成引數和返回值在函式之間進行傳遞時,編譯器利用閉包擴充套件變數的作用域,以保證隨時能得到所需要的資料。c 函式式程式設計之作用域 在c 中,變數的作用域是嚴格確定的。其本質是所有 生存在類的方法中 所有變數只生存於...