[lambda x: x*i for i in range(4)] 詳細解析+LEGB規則 閉包原理
轉自:http://www.cnblogs.com/shiqi17/p/9608195.html
一、問題描述
fun = [lambda x: x*i for i in range(4)]
for item in fun:
print(item(1))
上述式子的輸出結果:
預計結果為:0, 1, 2, 3
實際輸出為:3, 3, 3, 3
原理:i 在外層作用域
lambda x: x*i 為內層(嵌)函式,他的名稱空間中只有 {'x': 1} 沒有 i , 所以執行時會向外層函式(這兒是列表解析式函式 [ ])的名稱空間中請求 i 而當列表解析式執行時,列表解析式名稱空間中的 i 經過迴圈依次變化為 0->1->2->3 最後固定為 3 , 所以當 lambda x: x*i 內層函式執行時,去外層函式取 i 每次都只能取到 3
解決辦法:變閉包作用域為區域性作用域
給內層函式 lambda x: x*i 增加引數,名稱空間中有了用來儲存每次的 i , 即改成 [lambda x, i=i: x*i for i in range(4)] 這樣每一次,內部迴圈生成一個lambda 函式時, 都會把 --i--作為預設引數傳入lambda的名稱空間 迴圈4次實際lambda表示式為: 第一次:lambda x, i=0 第二次:lambda x, i=1 第三次:lambda x, i=2 第四次:lambda x, i=3 fun = [lambda x, i=i: x*i for i in range(4)] for item in fun: print(item(1)) #輸出結果為: 0 1 2 3
二、上面看不懂就看這兒
函式fun = [lambda x: x*i for i in range(4)]等價於:如下函式
def func():
fun_lambda_list = []
for i in range(4):
def lambda_(x):
return x*i
fun_lambda_list.append(lambda_)
return fun_lambda_list
檢視該函式名稱空間及 I 值變化:
def func(): fun_lambda_list = [] for i in range(4): def lambda_(x): print('Lambda函式中 i {} 名稱空間為:{}:'.format(i, locals())) return x*i fun_lambda_list.append(lambda_) print('外層函式 I 為:{} 名稱空間為:{}'.format(i, locals())) return fun_lambda_list fl = func() fl[0](1) fl[1](1) fl[2](1) fl[3](1) #執行結果為: 外層函式 I 為:0 名稱空間為:{'lambda_': <function func.<locals>.lambda_ at 0x7f7324d04158>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x7f7324d04158>], 'i': 0} 外層函式 I 為:1 名稱空間為:{'lambda_': <function func.<locals>.lambda_ at 0x7f7324d041e0>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x7f7324d04158>, <function func.<locals>.lambda_ at 0x7f7324d041e0>], 'i': 1} 外層函式 I 為:2 名稱空間為:{'lambda_': <function func.<locals>.lambda_ at 0x7f7324d04268>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x7f7324d04158>, <function func.<locals>.lambda_ at 0x7f7324d041e0>, <function func.<locals>.lambda_ at 0x7f7324d04268>], 'i': 2} 外層函式 I 為:3 名稱空間為:{'lambda_': <function func.<locals>.lambda_ at 0x7f7324d042f0>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x7f7324d04158>, <function func.<locals>.lambda_ at 0x7f7324d041e0>, <function func.<locals>.lambda_ at 0x7f7324d04268>, <function func.<locals>.lambda_ at 0x7f7324d042f0>], 'i': 3} Lambda函式中 i 3 名稱空間為:{'x': 2, 'i': 3}: Lambda函式中 i 3 名稱空間為:{'x': 2, 'i': 3}: Lambda函式中 i 3 名稱空間為:{'x': 2, 'i': 3}: Lambda函式中 i 3 名稱空間為:{'x': 2, 'i': 3}:
為了排版美觀,我已將輸出lambda_函式地址改名為:lam函式1 2 3
外層函式I為:0 名稱空間為:{'i': 0, 'lambda_': lam函式1 'fun_lambda_list': [lam函式1]}
外I:1 命空:{'i': 1, 'lambda_': lam函式2, 'fun_lambda_list': [lam函式1, lam函式2]}
外I:2 命空:{'i': 2, 'lambda_': lam函式3, 'fun_lambda_list': [lam函式1, lam函式2, lam函式3]}
外I:3 命空:{'i': 3, 'lambda_': lam函式4, 'fun_lambda_list': [lam函式1, lam函式2, lam函式3, lam函式4]}
Lambda函式中 i 3 名稱空間為:{'i': 3, 'x': 1}:
Lambda函式中 i 3 名稱空間為:{'i': 3, 'x': 1}:
Lambda函式中 i 3 名稱空間為:{'i': 3, 'x': 1}:
Lambda函式中 i 3 名稱空間為:{'i': 3, 'x': 1}:
可以看見:就像上面所說的:四次迴圈中外層函式名稱空間中的 i 從 0–>1–>2–>3 最後固定為3,
而在此過程中內嵌函式-Lambda函式中因為沒有定義 i 所以只有Lambda 函式動態執行時,
在自己名稱空間中找不到 i 才去外層函式複製 i = 3 過來,結果就是所有lambda函式的 i 都為 3,
導致得不到預計輸出結果:0,1,2,3 只能得到 3, 3, 3, 3
解決辦法:變閉包作用域為區域性作用域。
def func():
fun_lambda_list = []
for i in range(4):
def lambda_(x, i= i):
print('Lambda函式中 i {} 名稱空間為:{}:'.format(i, locals()))
return x*i
fun_lambda_list.append(lambda_)
return fun_lambda_list
fl = func()
res = []
res.append(fl[0](1))
res.append(fl[1](1))
res.append(fl[2](1))
res.append(fl[3](1))
print(res)
#輸出結果為:
Lambda函式中 i 0 名稱空間為:{'x': 1, 'i': 0}:
Lambda函式中 i 1 名稱空間為:{'x': 1, 'i': 1}:
Lambda函式中 i 2 名稱空間為:{'x': 1, 'i': 2}:
Lambda函式中 i 3 名稱空間為:{'x': 1, 'i': 3}:
[0, 1, 2, 3]
給內層函式 lambda_增加預設引數,名稱空間中有了用來儲存每次的 i , 即改成 def lambda_(x, i=i) : 這樣每一次,
內部迴圈生成一個lambda 函式時,都會把 i 作為預設引數傳入lambda的名稱空間
迴圈4次實際lambda表示式為:
第一次:lambda_( x, i=0) 第二次:lambda_(x, i=1) 第三次:lambda_(x, i=2) 第四次:lambda_(x, i=3)
這樣我們就能得到預計的結果:0, 1, 2, 3
LEGB
只有函式、類、模組會產生作用域,程式碼塊不會產生作用域。作用域按照變數的定義位置可以劃分為4類:
Local(函式內部)區域性作用域
Enclosing(巢狀函式的外層函式內部)巢狀作用域(閉包)
Global(模組全域性)全域性作用域
Built-in(內建)內建作用域
python直譯器查詢變數時,會按照順序依次查詢區域性作用域—>巢狀作用域—>全域性作用域—>內建作用域,在任意一個作用域中找到變數則停止查詢,所有作用域查詢完成沒有找到對應的變數,則丟擲 NameError: name ‘xxxx’ is not defined的異常。
人生還有意義。那一定是還在找存在的理由