在乙個函式內部定義另乙個函式,外部的函式為外函式,內部的函式為內函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用。這就形成了乙個閉包。
通常情況下,乙個函式執行結束後,函式內部的所有東西都會被釋放掉,區域性變數也會消失。但是如果外函式在結束時發現自己的臨時變數會在內函式中用到時,就會把這個臨時變數繫結給內函式,然後外函式才結束。
有一道很經典的案例:
def multipliers():
return [lambda x: i * x for i in range(4)]
print ([m(2) for m in multipliers()])
可能我們會認為,上面的函式將輸出為[0, 2, 4, 6],但其實輸出的是[6, 6, 6, 6],這就是閉包導致的。
閉包函式返回內部函式的引用(即定義的lambda匿名函式),外函式呼叫結束時會將被內函式引用的區域性變數(亦稱為閉包變數)繫結至內函式。而延遲繫結指的是,只有在呼叫內函式時,才會訪問閉包變數所指向的物件,不呼叫時不會訪問閉包變數所指向的物件。
上面的例子相當於:
def multipliers():
funcs =
for i in range(4):
def bar(x):
return x*i
return funcs
print ([m(2) for m in multipliers()] )
來看下不呼叫內函式的情況:
def multipliers():
result =
for i in range(4):
print(i)
def func(x):
print("test")
return i * x
return result
multipliers()
輸出為
012
3
可以從上面的**看出,未輸出test字元,說明我們沒有呼叫內函式,在我們沒有呼叫內函式的同時,迴圈的 i 變數最後指向了3,當外函式發現自己的臨時變數會在內函式中用到時,就把i=3繫結給了內函式,這時外函式已經呼叫結束。所以當我們呼叫內函式時,相當於這段偽**(不能執行的**):
return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]
所以輸出的結果為[6,6,6,6]
將每個迴圈的 i 值繫結給內函式就行了,也叫做立即繫結
多加個預設引數 i=i
def multipliers():
return [lambda x,i=i: i * x for i in range(4)]
相當於:
def multipliers():
funcs =
for i in range(4):
def bar(x, i=i):
return x * i
return funcs
print ([m(2) for m in multipliers()] )
這樣子就把迴圈過程的每個 i 變數都繫結到內函式了,相當於這段偽**:
return [lambda x: 0 * x, lambda x: 1 * x, lambda x: 2 * x, lambda x: 3 * x]
def multipliers():
for i in range(4):
yield lambda x : i * x
print([m(2) for m in multipliers()])
Python閉包的延遲繫結
1.什麼是閉包,閉包必須滿足以下3個條 2.閉包的優點def add a def add b return a b return add ad 2 2 計算2 2的值,用類實現的話,相對麻煩 閉包使用nonlocal deftester start state start defnested lab...
python 閉包 乙個很小知識點
修改前 返回x的乘法函式,是函式,呼叫才執行 defmake return lambda x i x for i in range 3 for r in make print r 4 期望輸出 04 8實際輸出 88 8原因 i因為閉包延遲繫結,到執行的時候已經為2 所以引數4一直只和最終的2相乘 ...
python包匯入的知識點
目錄 包匯入的搜尋路徑 init 檔案的作用 包內相對匯入 包匯入的搜尋路徑和python模組匯入的搜尋路徑和順序完全一樣,具體可以參看筆者的這篇文章。但是要注意的是,這裡的搜尋路徑指的是對最外層的那個包,例如from dir1.dir2 import mod語句,這裡的搜尋路徑指的是對dir1的搜...