最近看到了一個python程序題,就三行代碼,卻思考了好久才考慮明白,決定分享一下。python
def num(): return [lambda x:i*x for i in range(4)] print([m(2) for m in num()])
預計結果爲:0, 2, 4, 6
實際輸出爲:6, 6, 6, 6閉包
其實把上面的代碼拆分一下,等價於下面的代碼app
def func(): fun_lambda_list = [] for i in range(4): def lamb(x): return x*i fun_lambda_list.append(lamb) return fun_lambda_list
咱們再把上面的代碼加兩行print輸出,讓結果看的更加明顯:ide
PS:locals() 函數會以字典類型返回當前位置的所有局部變量。函數
def func(): fun_lambda_list = [] for i in range(4): def lamb(x): print('Lambda函數中 i {} 命名空間爲:{}:'.format(i, locals())) return x*i fun_lambda_list.append(lamb) print('外層函數 I 爲:{} 命名空間爲:{}'.format(i, locals())) return fun_lambda_list fl = func() fl[0](1) fl[1](1) fl[2](1) fl[3](1)
咱們會發現,打印的結果爲:ui
外層函數 I 爲:0 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x00000116B6837488>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x00000116B6837488>], 'i': 0} 外層函數 I 爲:1 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x00000116B6837510>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x00000116B6837488>, <function func.<locals>.lambda_ at 0x00000116B6837510>], 'i': 1} 外層函數 I 爲:2 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x00000116B6837598>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x00000116B6837488>, <function func.<locals>.lambda_ at 0x00000116B6837510>, <function func.<locals>.lambda_ at 0x00000116B6837598>], 'i': 2} 外層函數 I 爲:3 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x00000116B6837620>, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x00000116B6837488>, <function func.<locals>.lambda_ at 0x00000116B6837510>, <function func.<locals>.lambda_ at 0x00000116B6837598>, <function func.<locals>.lambda_ at 0x00000116B6837620>], 'i': 3} Lambda函數中 i 3 命名空間爲:{'x': 1, 'i': 3}: Lambda函數中 i 3 命名空間爲:{'x': 1, 'i': 3}: Lambda函數中 i 3 命名空間爲:{'x': 1, 'i': 3}: Lambda函數中 i 3 命名空間爲:{'x': 1, 'i': 3}:
能夠發現:四次循環中外層函數命名空間中的 i 從 0-->1-->2-->3 最後固定爲3,而在此過程當中內嵌函數lamb函數中由於沒有定義 i 因此只有lamb函數動態運行時,在本身命名空間中找不到 i 纔去外層函數複製 i = 3 過來,結果就是全部lamb函數的 i 都爲 3,致使得不到預計輸出結果:0,2,4,6 只能獲得 6,6,6,6。spa
變閉包做用域爲局部做用域3d
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_) 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 0x0000021F12227488>, 'i': 0, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x0000021F12227488>]} 外層函數 I 爲:1 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x0000021F12227510>, 'i': 1, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x0000021F12227488>, <function func.<locals>.lambda_ at 0x0000021F12227510>]} 外層函數 I 爲:2 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x0000021F12227598>, 'i': 2, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x0000021F12227488>, <function func.<locals>.lambda_ at 0x0000021F12227510>, <function func.<locals>.lambda_ at 0x0000021F12227598>]} 外層函數 I 爲:3 命名空間爲:{'lambda_': <function func.<locals>.lambda_ at 0x0000021F12227620>, 'i': 3, 'fun_lambda_list': [<function func.<locals>.lambda_ at 0x0000021F12227488>, <function func.<locals>.lambda_ at 0x0000021F12227510>, <function func.<locals>.lambda_ at 0x0000021F12227598>, <function func.<locals>.lambda_ at 0x0000021F12227620>]} Lambda函數中 i 0 命名空間爲:{'i': 0, 'x': 1}: Lambda函數中 i 1 命名空間爲:{'i': 1, 'x': 1}: Lambda函數中 i 2 命名空間爲:{'i': 2, 'x': 1}: Lambda函數中 i 3 命名空間爲:{'i': 3, 'x': 1}:
因此,再回到最開始的那段代碼。lambda x: x*i
爲內層(嵌)函數,他的命名空間中只有 {'x': 1} 沒有 i ,因此運行時會向外層函數(這兒是列表解析式函數 [ ])的命名空間中請求 i 。而當列表解析式運行時,列表解析式命名空間中的 i 通過循環依次變化爲 0-->1-->2-->3 最後固定爲 3 ,因此當 lambda x: x*i
內層函數運行時,去外層函數取 i 每次都只能取到 3。
code
將代碼改爲下面這樣輸出就會變成0,2,4,6.orm
def num(): return [lambda x,i=i:i*x for i in range(4)] print([m(2) for m in num()])
只有函數、類、模塊會產生做用域,代碼塊不會產生做用域。做用域按照變量的定義位置能夠劃分爲4類:
Local(函數內部)局部做用域 Enclosing(嵌套函數的外層函數內部)嵌套做用域(閉包) Global(模塊全局)全局做用域 Built-in(內建)內建做用域
python解釋器查找變量時,會按照順序依次查找局部做用域--->嵌套做用域--->全局做用域--->內建做用域,在任意一個做用域中找到變量則中止查找,全部做用域查找完成沒有找到對應的變量,則拋出 NameError: name 'xxxx' is not defined的異常。