python 通熟易懂的閉包

2021-07-23 19:31:37 字數 3285 閱讀 2630

#!/usr/bin/python  

# -*- coding: cp936 -*-

#python ver2.7

'''''

閉包(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 ax + 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傳遞來的引數,通過閉包的形式,將最終函式確定下來。

'''

#閉包與並行運算

'''''

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

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

'''

通熟易懂講控制代碼

所謂控制代碼實際上是乙個資料,是乙個long 整長型 的資料。控制代碼是wondows用來標識被應用程式所建立或使用的物件的唯一整數,windows使用各種各樣的控制代碼標識諸如應用程式例項,視窗,控制,位圖,gdi物件等等。windows控制代碼有點象c語言中的檔案控制代碼。從上面的定義中的我們可...

靜態主席樹 通熟易懂的解釋

學了三天主席樹,思維 了,不過收益良多,不只是主席樹,還有其他之類的。主席樹是什麼?想必你在看我部落格之前也看過不少大牛的部落格了,但是我提一下,主席樹就是用以解決類似於不斷的求區間內第幾 條件 大 小 自定義.的數或者狀態查詢。那麼我開始建樹講解了 我想要建樹,想要做到新建的鏈能做到字首和的作用,...

python通俗易懂的閉包

檢視官方解釋,閉包看起來確實不好理解,我通過查資料將閉包做乙個通俗的解釋 閉包是函式裡面再次定義乙個函式,外部的函式暫且稱為外函式,內部的函式暫且稱為內函式。外函式的返回值為內函式和它的環境,它的環境就是外函式的區域性變數 也是內函式的外部變數 當前的值,比如當前變數的值,很多教程裡將閉包的時候會用...