一步一步教你認識Python閉包

2021-08-08 18:18:13 字數 2644 閱讀 2617

我在部落格中曾經介紹過兩篇關於函式的文章,第一篇是 關於 python 函式是第一類物件,第二篇是關於 lambda 函式,今天來說說 python 閉包。

什麼是閉包?閉包有什麼用?為什麼要用閉包?今天我們就帶著這3個問題來一步一步認識閉包。

閉包和函式緊密聯絡在一起,介紹閉包前有必要先介紹一些背景知識,諸如巢狀函式、變數的作用域等概念

作用域是程式執行時變數可被訪問的範圍,定義在函式內的變數是區域性變數,區域性變數的作用範圍只能是函式內部範圍內,它不能在函式外引用。

定義在模組最外層的變數是全域性變數,它是全域性範圍內可見的,當然在函式裡面也可以讀取到全域性變數的。例如:

num = 10 # 全域性作用域變數

def foo():

print(num) # 10

而在函式外部則不可以訪問區域性變數。例如:

def foo():

num = 10

print(num) # nameerror: name 'num' is not defined

函式不僅可以定義在模組的最外層,還可以定義在另外乙個函式的內部,像這種定義在函式裡面的函式稱之為巢狀函式(nested function)例如:

def print_msg():

# print_msg 是外圍函式

msg = "zen of python"

def printer():

# printer是巢狀函式

print(msg)

printer()

# 輸出 zen of python

print_msg()

對於巢狀函式,它可以訪問到其外層作用域中宣告的非區域性(non-local)變數,比如**示例中的變數msg可以被巢狀函式printer正常訪問。

那麼有沒有一種可能即使脫離了函式本身的作用範圍,區域性變數還可以被訪問得到呢?答案是閉包

函式身為第一類物件,它可以作為函式的返回值返回,現在我們來考慮如下的例子:

def print_msg():

# print_msg 是外圍函式

msg = "zen of python"

def printer():

# printer 是巢狀函式

print(msg)

return printer

another = print_msg()

# 輸出 zen of python

another()

這段**和前面例子的效果完全一樣,同樣輸出 "zen of python"。不同的地方在於內部函式printer直接作為返回值返回了。

一般情況下,函式中的區域性變數僅在函式的執行期間可用,一旦print_msg()執行過後,我們會認為msg變數將不再可用。然而,在這裡我們發現 print_msg 執行完之後,在呼叫 another 的時候 msg 變數的值正常輸出了,這就是閉包的作用,閉包使得區域性變數在函式外被訪問成為可能。

看完這個例子,我們再來定義閉包,維基百科上的解釋是:

在電腦科學中,閉包(closure)是詞法閉包(lexical closure)的簡稱,是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。

這裡的another就是乙個閉包,閉包本質上是乙個函式,它有兩部分組成,printer函式和變數msg。閉包使得這些變數的值始終儲存在記憶體中。

閉包,顧名思義,就是乙個封閉的包裹,裡面包裹著自由變數,就像在類裡面定義的屬性值一樣,自由變數的可見範圍隨同包裹,**可以訪問到這個包裹,**就可以訪問到這個自由變數。

閉包避免了使用全域性變數,此外,閉包允許將函式與其所操作的某些資料(環境)關連起來。這一點與物件導向程式設計是非常類似的,在面物件程式設計中,物件允許我們將某些資料(物件的屬性)與乙個或者多個方法相關聯。

一般來說,當物件中只有乙個方法時,這時使用閉包是更好的選擇。來看乙個例子:

def adder(x):

return x + y

adder5 = adder(5)

# 輸出 15

adder5(10)

# 輸出 11

adder5(6)

這比用類來實現更優雅,此外裝飾器也是基於閉包的一中應用場景。

所有函式都有乙個__closure__屬性,如果這個函式是乙個閉包的話,那麼它返回的是乙個由 cell 物件 組成的元組物件。cell 物件的cell_contents 屬性就是閉包中的自由變數。

>>> adder.__closure__

>>> adder5.__closure__

(,)>>> adder5.__closure__[0].cell_contents

5

這解釋了為什麼區域性變數脫離函式之後,還可以在函式之外被訪問的原因的,因為它儲存在了閉包的 cell_contents中了。

一步一步教你認識 Python 閉包

我在部落格中曾經介紹過兩篇關於函式的文章,第一篇是 關於 python 函式是第一類物件,第二篇是關於 lambda 函式,今天來說說 python 閉包。什麼是閉包?閉包有什麼用?為什麼要用閉包?今天我們就帶著這3個問題來一步一步認識閉包。閉包和函式緊密聯絡在一起,介紹閉包前有必要先介紹一些背景知...

一步一步教你認識Python閉包

什麼是閉包?閉包有什麼用?為什麼要用閉包?今天我們就帶著這3個問題來一步一步認識閉包。閉包和函式緊密聯絡在一起,介紹閉包前有必要先介紹一些背景知識,諸如巢狀函式 變數的作用域等概念 作用域是程式執行時變數可被訪問的範圍,定義在函式內的變數是區域性變數,區域性變數的作用範圍只能是函式內部範圍內,它不能...

一步一步教你配置vnc

配置 vnc遠端桌面連線 1.在命令列輸入 init 3 進入純字元介面 2.kill 掉所有與 x有關的程序 用命令ps aux grep x 然後kill 程序號1 程序號2 只要找到的程序都 kill 掉 如果不會使用 kill 命令,直接重啟電腦 3.首先檢查 vncserver 是否安裝使...