命名空間,閉包原理,參考點擊本文html
fun = [lambda x: x*i for i in range(4)] for item in fun: print(item(1))
上述式子的輸出結果:
預計結果爲:0, 2, 4, 6
實際輸出爲:3, 3, 3, 3python
原理: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=3app
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 值變化:ui
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)
#運行結果爲:爲了排版美觀,我已將輸出lambda_函數地址更名爲:lam函數1 2 3spa
外層函數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, 3code
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)orm
這樣咱們就能獲得預計的結果:0, 1, 2, 3htm
只有函數、類、模塊會產生做用域,代碼塊不會產生做用域。做用域按照變量的定義位置能夠劃分爲4類:
Local(函數內部)局部做用域 Enclosing(嵌套函數的外層函數內部)嵌套做用域(閉包) Global(模塊全局)全局做用域 Built-in(內建)內建做用域
python解釋器查找變量時,會按照順序依次查找局部做用域--->嵌套做用域--->全局做用域--->內建做用域,在任意一個做用域中找到變量則中止查找,全部做用域查找完成沒有找到對應的變量,則拋出 NameError: name 'xxxx' is not defined的異常。