案例詳析 Python閉包與nonlocal關鍵字

2021-10-06 07:31:47 字數 3932 閱讀 9368

在廖雪峰的官網上看到乙個很有意思題目。關於閉包的,有興趣的朋友可以看一下, 做一下這個題目,當然需要一點閉包的知識。

下面我簡述一下:

利用閉包返回乙個計數器函式,每次呼叫它返回遞增整數。

# 修改下面這個函式

def createcounter():

def counter():

pass

return counter

# 測試:

countera = createcounter()

print(countera(), countera(), countera(), countera(), countera()) # 1 2 3 4 5

counterb = createcounter()

if [counterb(), counterb(), counterb(), counterb()] == [1, 2, 3, 4]:

print('測試通過!')

else:

print('測試失敗!')

方法一

說實話這題對我來說還是有點難度的,但我嘗試了幾次之後也找到乙個比較track的方法。一開始我是這麼寫的。

def createcounter():

i = 0

def counter(i=i):

i = i+1

return i

return counter

# 執行結果是:1 1 1 1 1

這樣當然是錯的, 因為整數是不可變物件,當你作為引數傳進去時都會建立乙個新的記憶體空間。

這裡邊其實還有很多學問,不是很了解的可以看一下stackoverflow上的這個回答:

雖然失敗了,但也讓我想到乙個track的方法,就是把i換成可變物件。

def createcounter():

i = [0]

def counter():

i[0] = i[0]+1

return i[0]

return counter

# 執行結果是:1 2 3 4 5

ok, 這樣就沒有問題了。但這並不是乙個好的解決方法, 利用可變物件的這個特性有可能會引起變數作用域混亂的。於是我又想到了另一種解決。

方法二另一種方法就是使用generator,在createcounter函式下建立乙個從1開始的整數generator, 然後在cuonter函式中呼叫。

由於generator儲存的是演算法,當呼叫next函式時就可以計算出下乙個的值,直到沒有元素報錯。當然這裡不用擔心,generator可以建立無限集合。

def createcounter():

def inter():

n = 1

while true:

yield n

n = n+1

f = inter()

def counter():

return next(f)

return counter

上面的**中,inter()就是乙個包含從1開始的所有整數的generator。然後在counter裡邊呼叫。每次計算下乙個的值。這樣就可以實現計數的功能。

說到generator,stackoverflow上有乙個回答值得一讀,即使你已經掌握這個也可以讀一下,這個回答應該還是python問答當中排名第一的。

鏈結在這裡:

方法三

def creat_counter():

i=0def counter():

nonlocal i

i=i+1

return i

return counter

學了python這麼久,第一次看到nonlocal這個關鍵字,果然我還是太菜了……

不過從語句上看nonlocal的作用應該是把i變成全域性變數,這樣每次修改都可以生效,跟global關鍵字有點像。既然找到乙個知識盲點,那就將它徹底解決吧。

nonlocal與global

說了這麼多,是時候回到主題了,nonlocal關鍵字到底是什麼?在什麼情況下用呢? 簡單來說,nonlocal關鍵字是用來改變變數的作用域的。

直接解釋不太好懂,先來看兩個例子吧。

def outside():

msg = "outside!"

def inside():

msg = "inside!"

print(msg)

inside()

print(msg)

執行結果是什麼呢?

inside!

outside!

結果應該很好理解, 在outside函式裡面定義了inside函式並且執行。當執行outside函式時,inside裡面的msg變數指向了"inside!",outside裡面的msg指向了"outside!", 也就是說這裡其實有兩個msg變數,並且指向了不同的值。如下圖所示:

再來看下面這個例子:

def outside():

msg = "outside!"

def inside():

nonlocal msg

msg = "inside!"

print(msg)

inside()

print(msg)

現在的執行結果就變成了:

inside!

inside!

兩段**之間的差別僅在於下面的例子多了一句 nonlocal msg。

這裡的nonlocal關鍵字起到了什麼作用呢?

nonlocal意思是告訴python,不要重新建立msg變數,而是使用outside中的msg變數來賦值。

畫個圖就很好懂了。

在這個例子中, msg變數只被建立了一次,首先將"outside!"賦值給msg,然後將"inside!"賦值給了msg, 此時的msg已經指向了"inside!"。因此執行的結果兩個都是"inside!"。

現在我們知道了,nonlocal是用來改變變數的作用域的。本例中,nonlocal將inside函式裡面的msg變數的作用域變成了outside塊中的區域。nonlocal跟global 這兩個關鍵字非常像,不同之處在於nonlocal用於外部函式作用域的變數,而global用於全域性範圍內的變數。 這就是nonlocal關鍵字的作用。但是還有一點值得注意,先看下面的例子。

def outside():

d =

def inside():

d["inside"] = 2

print(d)

inside()

print(d)

大家覺得輸出是什麼呢?

實際輸出是這樣的:

原因嘛,當然是因為dict是可變物件了,但由於 d["inside"] = 2 這樣的語句是有點讓人迷惑的,看起來很像重新賦值。實際上dict的賦值是呼叫了setitem方法。這樣看就不會感到迷惑了。

# 下面的**是等價的。

d["inside"] = 2

d.__setitem__("inside", 2)

關於nonlocal關鍵字,應該講清楚了吧。至於python的閉包,其實還是挺複雜的,上面的只是幾個例子,想要更加深入的學習可以上stackoverflow上看看大佬們的回答。很有學習的價值。

文源網路,僅供學習之用,侵刪。

閉包小案例

閉包可以用在許多地方。它的最大用處有兩個,乙個是可以讀取函式內部的變數,另乙個就是讓這些變數的值始終保持在記憶體中。閉包的應用涉及作用域 函式的引數傳遞 變數提公升等知識,當迴圈使用時,通過畫圖的方式可以更好的去理解。以下的這段 n 只有1級引用 function fun n,s var a fun...

python 閉包 python 閉包

閉包 因為python中函式也可以當作物件,所以如果出現當我們返回乙個函式,而該函式含有外部變數的時候就形成了閉包。閉包的特點 是個巢狀函式 可以獲得非區域性的變數 將函式當作物件返回 看乙個例子會更易理解 def make printer msg msg hi there def printer ...

Python高階特性之閉包與裝飾器例項詳解

閉包 1.函式引數 1 函式名存放的是函式的位址 2 函式名 存放的是函式內的 3 函式名只是函式 空間的引用,當函式名賦值給乙個物件的時候,就是引用傳遞 def func01 print func01 is show test func01 print func01 print test test...