Python深入分享之閉包
閉包(closure)是函式語言程式設計的重要的語法結構。函式語言程式設計是一種程式設計正規化 (而程式導向程式設計和麵向物件程式設計也都是程式設計正規化)。在程式導向程式設計中,我們見到過函式(function);在物件導向程式設計中,我們見過物件(object)。函式和物件的根本目的是以某種邏輯方式組織程式碼,並提高程式碼的可重複使用性(reusability)。閉包也是一種組織程式碼的結構,它同樣提高了程式碼的可重複使用性。
不同的語言實現閉包的方式不同。Python以函式物件為基礎,為閉包這一語法結構提供支援的 (我們在特殊方法與多正規化中,已經多次看到Python使用物件來實現一些特殊的語法)。Python一切皆物件,函式這一語法結構也是一個物件。在函式物件中,我們像使用一個普通物件一樣使用函式物件,比如更改函式物件的名字,或者將函式物件作為引數進行傳遞。
函式物件的作用域
和其他物件一樣,函式物件也有其存活的範圍,也就是函式物件的作用域。函式物件是使用def語句定義的,函式物件的作用域與def所在的層級相同。比如下面程式碼,我們在line_conf函式的隸屬範圍內定義的函式line,就只能在line_conf的隸屬範圍內呼叫。
def line_conf(): def line(x): return 2*x+1 print(line(5)) # within the scope line_conf() print(line(5)) # out of the scope
line函式定義了一條直線(y = 2x + 1)。可以看到,在line_conf()中可以呼叫line函式,而在作用域之外呼叫line將會有下面的錯誤:
NameError: name 'line' is not defined
說明這時已經在作用域之外。
同樣,如果使用lambda定義函式,那麼函式物件的作用域與lambda所在的層級相同。
閉包
函式是一個物件,所以可以作為某個函式的返回結果。
def line_conf(): def line(x): return 2*x+1 return line # return a function object my_line = line_conf() print(my_line(5))
上面的程式碼可以成功執行。line_conf的返回結果被賦給line物件。上面的程式碼將列印11。
如果line()的定義中引用了外部的變數,會發生什麼呢?
def line_conf(): b = 15 def line(x): return 2*x+b return line # return a function object b = 5 my_line = line_conf() print(my_line(5))
我們可以看到,line定義的隸屬程式塊中引用了高層級的變數b,但b資訊存在於line的定義之外 (b的定義並不在line的隸屬程式塊中)。我們稱b為line的環境變數。事實上,line作為line_conf的返回值時,line中已經包括b的取值(儘管b並不隸屬於line)。
上面的程式碼將列印25,也就是說,line所參照的b值是函式物件定義時可供參考的b值,而不是使用時的b值。
一個函式和它的環境變數合在一起,就構成了一個閉包(closure)。在Python中,所謂的閉包是一個包含有環境變數取值的函式物件。環境變數取值被儲存在函式物件的__closure__屬性中。比如下面的程式碼:
def line_conf(): b = 15 def line(x): return 2*x+b return line # return a function object b = 5 my_line = line_conf() print(my_line.__closure__) print(my_line.__closure__[0].cell_contents)
__closure__裡包含了一個元組(tuple)。這個元組中的每個元素是cell型別的物件。我們看到第一個cell包含的就是整數15,也就是我們建立閉包時的環境變數b的取值。
下面看一個閉包的實際例子:
def line_conf(a, b): def line(x): return a*x + b return line line1 = line_conf(1, 1) line2 = line_conf(4, 5) print(line1(5), line2(5))
這個例子中,函式line與環境變數a,b構成閉包。在建立閉包的時候,我們透過line_conf的引數a,b說明瞭這兩個環境變數的取值,這樣,我們就確定了函式的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換引數a,b,就可以獲得不同的直線表達函式。由此,我們可以看到,閉包也具有提高程式碼可複用性的作用。
如果沒有閉包,我們需要每次建立直線函式的時候同時說明a,b,x。這樣,我們就需要更多的引數傳遞,也減少了程式碼的可移植性。利用閉包,我們實際上建立了泛函。line函式定義一種廣泛意義的函式。這個函式的一些方面已經確定(必須是直線),但另一些方面(比如a和b引數待定)。隨後,我們根據line_conf傳遞來的引數,透過閉包的形式,將最終函式確定下來。
閉包與並行運算
閉包有效的減少了函式所需定義的引數數目。這對於並行運算來說有重要的意義。在並行運算的環境下,我們可以讓每臺電腦負責一個函式,然後將一臺電腦的輸出和下一臺電腦的輸入串聯起來。最終,我們像流水線一樣工作,從串聯的電腦叢集一端輸入資料,從另一端輸出資料。這樣的情境最適合只有一個引數輸入的函式。閉包就可以實現這一目的。
並行運算正稱為一個熱點。這也是函式語言程式設計又熱起來的一個重要原因。函式語言程式設計早在1950年代就已經存在,但應用並不廣泛。然而,我們上面描述的流水線式的工作並行叢集過程,正適合函式語言程式設計。由於函式語言程式設計這一天然優勢,越來越多的語言也開始加入對函式語言程式設計正規化的支援。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70026630/viewspace-2996324/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入學習js之——閉包#8JS
- 深入理解閉包
- Python基礎之閉包函式Python函式
- Python深入分享之裝飾器Python
- 深入理解JS閉包JS
- 深入淺出Javascript閉包JavaScript
- Python深入分享之物件的屬性Python物件
- 深入理解swift的閉包Swift
- 零基礎學習 Python 之閉包Python
- Python進階之閉包和裝飾器Python
- JavaScript之閉包JavaScript
- Javascript深入之作用域與閉包JavaScript
- Python高階--閉包Python
- Python技法4:閉包Python
- Python分享之數學與隨機數 (math包,random包)Python隨機random
- Python標準庫分享之儲存物件 (pickle包,cPickle包)Python物件
- python基礎知識之函式初階——閉包Python函式
- 深入理解javascript系列(七):閉包(1)JavaScript
- 深入理解javascript系列(八):閉包(2)JavaScript
- 理解Python函式閉包Python函式
- python進階(12)閉包Python
- Python標準庫分享之檔案管理 (部分os包,shutil包)Python
- javascript之溫習閉包JavaScript
- 好程式設計師大資料教程分享Scala系列之閉包程式設計師大資料
- 深入理解javascript系列(九):應用閉包JavaScript
- Python學習筆記 - 閉包Python筆記
- 【python】閉包與裝飾器Python
- python閉包 - 理解與應用Python
- Python 閉包函式說明Python函式
- Python裡的閉包和AOPPython
- python closure閉包 lambda表示式Python
- Python閉包與裝飾器Python
- Python中什麼是閉包?閉包的好處是什麼?Python
- Python3之從遞迴到閉包再到裝飾器Python遞迴
- 草根學Python(十五) 閉包(解決一個需求瞭解閉包流程)Python
- Python:從閉包到裝飾器Python
- python高階-閉包-裝飾器Python
- python的裝飾器和閉包Python