函式高階(閉包)

2022-03-25 09:42:15 字數 2898 閱讀 9791

def outer_func():

loc_list =

def inner_func(name):

print('%s loc_list = %s' %(name, loc_list))

return inner_func

clo_func_0 = outer_func()

clo_func_0('clo_func_0')

clo_func_0('clo_func_0')

clo_func_0('clo_func_0')

clo_func_0('clo_func_0')

clo_func_1 = outer_func()

clo_func_1('clo_func_1')

clo_func_1('clo_func_1')

程式執行結果:

clo_func_0 loc_list = [1]

clo_func_0 loc_list = [1, 2]

clo_func_0 loc_list = [1, 2, 3]

clo_func_0 loc_list = [1, 2, 3, 4]

clo_func_1 loc_list = [1]

clo_func_1 loc_list = [1, 2]

這是乙個閉包函式,但是引用的變數是個列表型,也就是說這個變數是乙個可變型別,每次呼叫函式,其結果都會變化。

同下例的簡單函式:

#函式的魔性寫法

def func(lst=):

print(lst)

# 預設引數盡量避免使用可變的型別

func()

func()

func()

func()

#呼叫結果

[1][1, 1]

[1, 1, 1]

[1, 1, 1, 1]

在閉包函式例子中我們至少可以對閉包中引用的自由變數有如下的認識:

由於這個概念理解起來並不是那麼的直觀,因此使用的時候很容易掉進陷阱。

閉包函式陷阱:

def my_func(*args):

fs =

for i in range(3):

def func():

return i * i

return fs

fs1, fs2, fs3 = my_func()

print (fs1())

print (fs2())

print (fs3())

#執行結果44

4

上面這段**可謂是典型的錯誤使用閉包的例子。程式的結果並不是我們想象的結果0,1,4。實際結果全部是4。

這個例子中,my_func返回的並不是乙個閉包函式,而是乙個包含三個閉包函式的乙個list。這個例子中比較特殊的地方就是返回的所有閉包函式均引用父函式中定義的同乙個自由變數。

但這裡的問題是為什麼for迴圈中的變數變化會影響到所有的閉包函式?尤其是我們上面剛剛介紹的例子中明明說明了同一閉包的不同例項中引用的自由變數互相沒有影響的。而且這個觀點也絕對的正確。

那麼問題到底出在**?應該怎樣正確的分析這個錯誤的根源。

其實問題的關鍵就在於在返回閉包列表fs之前for迴圈的變數的值已經發生改變了,而且這個改變會影響到所有引用它的內部定義的函式。因為在函式my_func返回前其內部定義的函式並不是閉包函式,只是乙個內部定義的函式。

當然這個內部函式引用的父函式中定義的變數也不是自由變數,而只是當前block中的乙個local variable。

def my_func(*args):

fs =

j = 0

for i in range(3):

def func():

return j * j

j = 2

return fs

f = my_func()

print(f)

#執行結果:

[.func at 0x00000000021107b8>, .func at 0x0000000002110840>,

.func at 0x00000000021108c8>]

上面的這段**邏輯上與之前的例子是等價的。這裡或許更好理解一點,因為在內部定義的函式func實際執行前,對區域性變數j的任何改變均會影響到函式func的執行結果。

函式my_func一旦返回,那麼內部定義的函式func便是乙個閉包,其中引用的變數j成為乙個只和具體閉包相關的自由變數。後面會分析,這個自由變數存放在cell物件中。

使用lambda(蘭布達)表示式重寫這個例子:

def my_func(*args):

fs =

for i in range(3):

func = lambda : i * i

return fs

經過上面的分析,我們得出下面乙個重要的經驗:返回閉包中不要引用任何迴圈變數,或者後續會發生變化的變數。

這條規則本質上是在返回閉包前,閉包中引用的父函式中定義變數的值可能會發生不是我們期望的變化。

def my_func(*args):

fs =

for i in range(3):

def func(_i = i):

return _i * _i

return fs

#或者

正確的做法便是將父函式的local variable賦值給函式的形參。函式定義時,對形參的不同賦值會保留在當前函式定義中,不會對其他函式有影響。

另外注意一點,如果返回的函式中沒有引用父函式中定義的local variable,那麼返回的函式不是閉包函式。

閉包函式的應用:

高階函式 閉包

f用於 的格式化,內是變數或函式的某個形參 s這種格式化不行 def func name 小明 print f good morning,func def func name print f good morning,func 小明 分割線 高階函式 引數為函式 閉包 返回值為函式 裝飾器 返回值為...

Python 高階函式 閉包

閉包 什麼是閉包 如果在乙個內部函式裡,對在外部作用域 但不是在全域性作用域 的變數進行引用,那麼內部函式就被認為是閉包。需要滿足的條件 1 乙個函式內巢狀乙個函式 2 外部函式的返回值必須是這個內部函式 3 內部函式必須使用外部函式中的區域性變數 def fun 1 num 1 9 def fun...

11 函式高階 閉包

內部函式對外部函式作用域裡變數的引用 非全域性變數 則稱內部函式為閉包。def outer n 10 def inner print inner n return inner val outer print val val 閉包的意義 返回的函式物件,不僅僅是乙個函式物件,在該函式外還包裹了一層作用...