程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

自學Python 35 閉包:函數和利用引用環境組合而成的實體

編輯:Python

Python 閉包


文章目錄

  • Python 閉包
  • 一、什麼是閉包
  • 二、閉包和嵌套函數
  • 三、使用閉包記錄函數被調用的次數


在計算機編程應用中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以還有另一種說法,認為閉包是由函數和其相關的引用環境組合而成的實體。閉包在運行時可以有多個實例,不同的引用環境和相同的函數組合可以產生個同的實例。


一、什麼是閉包

根據字面意思,可以形象地把閉包理解為一個封閉的包裹,這個包裹就是一個函數,當然還有函數內部對應的邏輯,包裹裡面的東西就是自由變量,自由變量可以隨著包裹到處游蕩。當然還得有個前提,這個包裹是被創建出來的。在Python語言中,一個閉包就是你調用了一個函數A,這個函數A返回了一個函數B給你。這個返回的函數B就叫作閉包。你在調用函數A的時候,傳遞的參數就是一個自由變量。例如在下面的實例代碼中演示了生成一個閉包的具體過程

def func (name):
def inner_func (date):
print ('name: ',name, 'date:', date)
return inner_func
bb= func('暑假')
bb (71)

在上述實例代碼中,當調用函數 func(的時候就產生了一個閉包:inner_func(),並且該閉包擁有自由變量“name”。這表示當函數func()的生命周期結束之後,變量name會依然存在,因為它被閉包引用了,所以不會被回收。執行後會輸

注意:閉包並不是Python語言所特有的概念,所有把函數作為“一等公民”的語言均有閉包這一概念。不過像Java這樣以 class為“一等公民”的語言中也可以使用閉包,只是它得用類或接口來實現。

另外,如果從表現形式上來講解 Python 中的閉包,表示如果在一個內部函數裡,對外部作用域(但個是在全局作用域)的變量進行引用,那麼內部函數就被認為是閉包。這種解釋非常容易理解,不像其他定義那樣有一堆陌生名詞,不適合初學者。
注意:閉包和類的異同
基於前面內容的介紹,相信讀者已經發現閉包和類有點相似,相似點在於它們都提供了對數據的封裝。不同的是閉包本身就是個方法。和類一樣,我們在編程時經常會把通用的東西抽象成類(當然,還有對現實世界—業務的建模),以復用通用的功能。閉包也是一樣,當我們需要函數粒度的抽象時,閉包就是一個很好的選擇。在這點上閉包可以被理解為一個只讀對象,你可以給它傳遞一個屬性,但它只能提供給你一個執行的接口。因此在程序中我們經常需要這樣的一個函數對象——閉包來幫我們完成一個通用的功能,比如之前講到的裝飾器。

二、閉包和嵌套函數

在Python語言中,閉包是指將組成函數的語句和這些語句的執行環境打包到一起所得到的對象。當使用嵌套函數(函數中定義函數)時,閉包將捕獲內部函數執行所需的整個環境。此外,嵌套函數可以使用被嵌套函數中的任何變量,就像普通函數可以引用全局變量一樣,而不需要通過參數引入。
例如在下面的實例代碼中,演示了嵌套函數可以使用被嵌套函數中的任何變量的過程。

x=14 #定義全局變量x
def foo() : #定義嵌套函數的外層函數foo()
x=50
def bar() : #定義一個變量x的初始值是3
print('x的值是:%d' %x) #定義嵌套的內層函數bar()
print('暑假的計劃是學%d' %x,'天') #引用的變量X
bar () #調用嵌套的內層函數bar()
if __name__ == '__main__':
foo() #調用嵌套函數的外層函數foo()

在上述實例代碼中定義了一個全局變量x,在嵌套函數的外層函數foo中也定義了一個變量x:在嵌套的內層函數bar()中引用的變量x應該是foo()中定義的x。因為嵌套函數可以直接引用其外層的函數中定義的變量x的值並輸出,所以輸出的值為50,而不是全局變量x的值14。執行後會輸出:

三、使用閉包記錄函數被調用的次數

為了深入理解Python閉包的知識,下面舉一個內外嵌套函數調用的例子。我們可以把這個實例看作是統計
個函數調用次數的函數。可以將count[0]看作是個計數器, 每執行一次函數hello(), count[0] 的值就加1。具體實現代碼如下所示。

def hellocounter (name) :
count=[0]
def counter () :
count[0]+=1
print ('Hello, ',name, ',',str (count[0])+' access!')
return counter
hello = hellocounter ('暑假')
hello()
hello()
hello()

在上述實例代碼中,也許有的讀者會提出疑問:為什麼不直接寫count,而用一個列表實現呢?這其實是Python2的一個bug,如果不用列表的話,會報如下所示的錯誤。
UnboundLocalError: local variable 'count' referenced before assignment.
上述錯誤的意思是說變量count沒有定義就直接引用了,於是在Python 3中引入了一個關鍵字nonlocal,這個關鍵字的功能是告訴Python程序,這個count變量是在外部定義的,然後Python就會去外層函數查找變量count,然後就找到了count=0這個定 義和賦值,這樣程序就能正常執行了。執行後會輸出:


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved