匿名函數在列表生成式 (推導式) 中的應用產生的閉包函數延遲綁定問題
先看下面這個函數:
def num():
return [lambda x: x * i for i in range(4)]
print([func(2) for func in num()])
再不運行結果的情況下心算下它的結果......是不是算到 [0,2,4,6] ?
於是在pycharm里面運行了一下 : [6,6,6,6], 百思不得其解, 我們先來把匿名函數變成普通函數, 這樣好理解一點 :
def num():
sub=[]
for i in range(4):
def bar(x):
return i*x
sub.append(bar)
return sub
print([func(2) for func in num()])
我們可以看到它是一個閉包函數的結構, 看 num( ) 返回的列表里面放的應該是四個 bar 函數的內存地址 : [bar, bar, bar, bar] , 並沒有去調用 bar 函數
def num():
# 返回值是一個列表生成式, 里面存放了四個而函數對象
return [lambda x: x * i for i in range(4)]
for i in num():
print(i)
'''
<function num.<locals>.<listcomp>.<lambda> at 0x000002AA99D2CE58>
<function num.<locals>.<listcomp>.<lambda> at 0x000002AA99D2CDC8>
<function num.<locals>.<listcomp>.<lambda> at 0x000002AA99EE4708>
<function num.<locals>.<listcomp>.<lambda> at 0x000002AA99EE4D38>
'''
當執行print([func(2) for func in num()])
的時候, 這才觸發了每個 bar 函數的執行, 此時的 for 循環已經結束了, 最終的 i = 3, 所以每次的結果都是 2*3=6
非匿名函數解決方法
# 直接在匿名函數內綁定默認參數i
def num():
return [lambda x,n=i: x * n for i in range(4)]
print([func(2) for func in num()]) # [0, 2, 4, 6]
非匿名函數解決方法
在內層函數 bar 形參位置設置一個接收 i 的參數, 讓其綁定
def num():
sub=[]
for i in range(4):
def bar(x,n=i):
return n*x
sub.append(bar)
return sub
print([func(2) for func in num()]) # [0, 2, 4, 6]
閉包函數延遲綁定問題解釋
在函數嵌套的時候, 如果一個循環返回的是一個函數對象, 該函數對象出現引用的時候並不會立即引用循環的值, 而是在運行(調用)嵌套函數的時候才會去查找該引用的值, 這個特性就是閉包函數的延遲綁定