for循環中的lambda與閉包——Python的閉包是 遲綁定 , 這意味着閉包中用到的變量的值,是在內部函數被調用時查詢


for循環中的lambda與閉包

問題引入

z = [lambda x:x*i for i in range(3)] x = [o(2) for o in z] print x # [4,4,4] 

f = [lambda x: x*i for i in range(3)](與x無關)

i就是在閉包作用域(enclousing),而Python的閉包是 遲綁定 , 這意味着閉包中用到的變量的值,是在內部函數被調用時查詢得到的

調用結果

>>> f = [lambda x:x*i for i in range(3)] >>> f [<function <listcomp>.<lambda> at 0x00000237F7DECD08>, <function <listcomp>.<lambda> at 0x00000237F7DECD90>, <function <listcomp>.<lambda> at 0x00000237F7DECE18>] >>> f[0](3) 6 # 2 * 3 >>> f[1](3) 6 # 2 * 3 >>> f[2](3) 6 # 2 * 3 >>> f[0](1) 2 # 2 * 1 >>> f[1](1) 2 # 2 * 1 >>> f[2](1) 2 # 2 * 1 

當調用 func() 時,每循環一次,將 lam 函數的地址存到 fs 中。因為在每次循環中 lam函數都未綁定 i 的值,所以直到循環結束,i 的值為2,並將 lam 中所用到的 i 值定為 2 ,因此真正調用(例如f[0](2))的時候 i 值保持不變(為2),如下。

表達式用普通函數表示為:

def func(): fs = [] for i in range(3): print('進入lam前i的值:', i) def lam(x): print('進入lam后i的值:', i) return x * i fs.append(lam) return fs F = func() print(F) """ 進入lam前i的值: 0 進入lam前i的值: 1 進入lam前i的值: 2 [<function func.<locals>.lam at 0x000002B4845ACAE8>, <function func.<locals>.lam at 0x000002B4845ACB70>, <function func.<locals>.lam at 0x000002B4845ACBF8>] """ # 將x的值傳入 for f in F: print(f(2)) """ 進入lam后i的值: 2 4 進入lam后i的值: 2 4 進入lam后i的值: 2 4 """ 

f = [lambda :i*3 for i in range(3)](與x無關)

另一種將x換成i,就與傳入的x值無關了。(這里 lambda 后面什么參數都不跟)

>>> f = [lambda :i*3 for i in range(3)] >>> f[0]() 6 >>> f[1]() 6 >>> f[2]() 6 

普通函數表示如下:

def func(): fs = [] for i in range(3): print('進入lam前i的值:', i) def lam(): print('進入lam后i的值:', i) return i * 3 fs.append(lam) return fs F = func() print(F) """ 進入lam前i的值: 0 進入lam前i的值: 1 進入lam前i的值: 2 [<function func.<locals>.lam at 0x000001842141CAE8>, <function func.<locals>.lam at 0x000001842141CD90>, <function func.<locals>.lam at 0x000001842141CE18>] """ for f in F: print(f()) """ 進入lam后i的值: 2 6 進入lam后i的值: 2 6 進入lam后i的值: 2 6 """ 

f = [lambda x, i=i:x*i for i in range(3)](與x有關)

變閉包作用域為局部作用域。其中,i=i前面的i就是局部作用域。

>>> f = [lambda x, i=i:x*i for i in range(3)] >>> f[0](3) 0 >>> f[1](3) 3 >>> f[2](3) 6 

換種寫法:

def func(): fs = [] for i in range(3): def lam(x, i=i): return x * i fs.append(lam) return fs F = func() for f in F: print(f(3)) """ 0 3 6 """ 

f = [lambda i=i: i*i for i in range(3)]

>>> f = [lambda i=i: i*i for i in range(3)] >>> f [<function <listcomp>.<lambda> at 0x00000237F7DECB70>, <function <listcomp>.<lambda> at 0x00000237F7DECAE8>, <function <listcomp>.<lambda> at 0x00000237F7DECBF8>] >>> f[0]() 0 >>> f[1]() 1 >>> f[2]() 4 

上面的表達式展開如下(為了更直觀,替換了變量):

def func(): fs = [] for i in range(3): print('進入lam前i的值:', i) def lam(x=i): print('進入lam后i的值:', i) return x * x fs.append(lam) return fs F = func() print(F) """ 進入lam前i的值: 0 進入lam前i的值: 1 進入lam前i的值: 2 [<function func.<locals>.lam at 0x0000025C91F9CC80>, <function func.<locals>.lam at 0x0000025C91F9CAE8>, <function func.<locals>.lam at 0x0000025C91F9CB70>] """ for f in F: print(f(8)) """ 進入lam后i的值: 2 0 進入lam后i的值: 2 1 進入lam后i的值: 2 4 """ 

當調用 func() 時,每循環一次,將 lam 函數的地址存到 fs 中。但是在每次循環中 lam函數都將 i 值綁定到了 x 上,所以直到循環結束,不同地址的 lam 函數的 x 值為都不一樣,因此真正調用(例如 f[0]())的時候 x 值都為當時被綁定的值。

但如果給 lam 函數傳了參數,例如 f[0](8),那么所有的調用結果都為傳參的平方。與上面解釋並不沖突,只是將傳的參數綁定到了 x 上。

>>> f = [lambda i=i: i*i for i in range(3)] >>> f[0](8) 64 >>> f[1](8) 64 >>> f[2](8) 64 

f = [lambda x=i: i*i for i in range(3)]

和第二種好像,只是變了一個字符,那么結果就大不一樣了。因為局部變量是x,i是閉包,會遲綁定。

對於上面的表達式,調用結果:

>>> f = [lambda x=i: i*i for i in range(3)] >>> f[0]() 4 >>> f[1]() 4 >>> f[2]() 4 >>> f[0](10) 4 >>> f[1](10) 4 >>> f[2](10) 4 

傳不傳參數都不影響結果。展開后:

def func(): fs = [] for i in range(3): def lam(x=i): return i * i fs.append(lam) return fs F = func() for f in F: print(f(), f(10)) """ 4 4 4 4 4 4 

雖然 lam 函數將 i 的值綁定到了 x 上,但函數體中並未使用 x,所以直到循環結束,i 的值變為2,才會在調用時使用。其實同第一種情況是一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM