630
技術社區[雲棲]
Python-關於Python中閉包的一些理解
看不懂的定義:閉包是由函數及其相關的引用環境組合而成的實體(即:閉包=函數+引用環境)。
既然是看不懂的定義,真看不懂上麵定義的話就忽略吧。
在python中,函數可以作為另一個函數的參數或返回值,可以賦給一個變量。函數可以嵌套定義,即在一個函數內部可以定義另一個函數,有了嵌套函數這種結構,便會產生閉包問題。
好理解一點的定義:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麼內部函數就被認為是閉包(closure)
舉個栗子:
1
2
3
4
|
def outer(x):
def inner(y):
return x + y
return inner
|
結合代碼分析定義:
— inner()就是內部函數。
— x就是被引用的變量,x在外部作用域,但不在全局作用域。
—- inner就是一個閉包。
如果你想學習Python可以來這個群,首先是四七二,中間是三零九,最後是二六一,裏麵有大量的學習資料可以下載。
關於閉包很難理解的一個問題,我嚐試用圖形化思維來理解
先看一個簡單的循環
1
2
|
for i in range(3):
print i
|
在程序裏麵經常會出現這類的循環語句,python的一個現象是,當循環結束以後,循環體中的臨時變量i不會銷毀,而是繼續存在於執行環境中。還有一個python的現象是,python的函數隻有在執行時,才會去找函數體裏的變量的值。
這段話特別需要記住兩點:
- 當循環結束時,循環體中的臨時變量i不會銷毀
- python的函數隻有在執行時,才會去找函數體裏的變量的值
記住上麵兩點後,下麵看經典的難理解的栗子:
1
2
3
4
5
6
7
8
9
|
def foo():
func_list = []
for i in range(3):
def inner():
return i*i
func_list.append(inner)
return func_list
f = foo()
|
在這個例子中,每次循環都創建一個新的函數,並且將創建的三個都添加到這個列表中
這裏調用foo(),f中就保存了一個列表對象,這個列表中保存了3個函數對象。
不妨打印一下看看 f 中三個元素的值:
1
2
3
4
|
>>> print f[0],'\n',f[1],'\n',f[2]
<function inner at 0x000000000263BAC8>
<function inner at 0x0000000002E664A8>
<function inner at 0x0000000002E66518>
|
從打印信息可以看出,f 中存放了3個函數名相同,但內存地址不同,的函數對象。
此時調用一下三個函數
1
2
3
4
5
6
|
>>> f[0]()
4
>>> f[1]()
4
>>> f[2]()
4
|
可能有些人認為這段代碼的執行結果應該是0,1,4.但是實際的結果是4,4,4。這是因為當把函數對象加入func_list列表裏時,python還沒有給i賦值,隻有當執行時,再去找i的值是什麼,這時在第一個for循環結束以後,i的值是2,所以以上代碼的執行結果是4,4,4.
不好理解的話,畫個流程圖():
這裏也可以直觀的理解文章開始提到的閉包的定義公式()
結果全部都是 ,原因就在於返回的函數引用了變量i,但它並非立刻執行。等到3個函數被調用時,它們所引用的變量i已經變成了2,因此最終結果為4。
返回閉包時牢記的一點就是:。
如果要正確的輸出引用循環變量後的值,隻需要將每次循環變量鎖定到閉包中,具體實現如下:
1
2
3
4
5
6
7
8
9
|
def foo():
func_list = []
for i in range(3):
def inner( x = i):
print x*x
func_list.append(inner)
return func_list
f = foo()
|
這樣的話,打印調用每個閉包後的結果為
1
2
3
4
5
6
|
>>> f[0]()
0
>>> f[1]()
1
>>> f[2]()
4
|
最後更新:2017-04-09 22:30:34