閱讀630 返回首頁    go 技術社區[雲棲]


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的函數隻有在執行時,才會去找函數體裏的變量的值。

這段話特別需要記住兩點:

  1. 當循環結束時,循環體中的臨時變量i不會銷毀
  2. 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

  上一篇:go Docker Swarm運行Spring Cloud應用(二):Eureka高可用
  下一篇:go Docker Swarm運行Spring Cloud(一):部署