有這麼一個小面試題:面試
看下面代碼請回答輸出的結果是什麼?爲何?express
result = [lambda x: x + i for i in range(10)] print(result[0](10))
當你看到這篇文章的時候若是不知道這個知識點確定會拿去直接運行,輸出的結果是什麼呢?閉包
結果是:19app
經過result[0~9](10)結果都是19就,懵逼了吧~~函數
想知道這個咱們先看幾個知識點spa
顧名思義,列表生成式就是一個用來生成列表的特定語法形式的表達式。翻譯
語法格式:code
[expression for iterable_var in iterable]
工做過程:blog
工做過程相似於:it
L = [] for i in range(10): # 舉例expression 表達式 iterable_var * 5 ,最後把這個結果加到列表中 L.append(i * 5)
點到爲止,咱們來看下咱們的這個小面試題
result = [lambda x: x + i for i in range(10)] # 後面的lambada x:x +i 爲expression 這是一個普通的lambada表達式那他生成的結果是什麼? 一個一個的函數 # 看下面的例子就舒服多了 L = [] for i in range(10): L.append(lambda x: x + i)
那在咱們執行result[0](10),其實就是在執行lambda 10: 10 + i ,可是爲何每一個i都是9呢?
咱們在寫一個函數的時候,函數內不保存這個變量的值,而是在執行的時候去找這個值在哪裏綁定上的。
舉個例子來講咱們在函數中定引用了一個變量,能夠不須要提早定義咱們只要在使用前定義就能夠了,以下面代碼
def func(): print(foo) foo = "hello tim" func()
注:這裏有個點稍微提醒下,調用函數時這個函數必須是提早定義好的,這個咱們稱之爲「前向引用」,而函數內部變量能夠後期再定義,咱們稱之爲「後期綁定」
由於這個變量不是在lambda內定義的,而是在列表生成式內定義的,我先把列表生成式翻譯一下:
def make_list(): L =[] for i in range(10): def inner(arg): return i + arg L.append(inner) return L result = make_list() print(result[2](10))
看上面的代碼最後返回的都是一個一個的函數,而這個i的值是在循環的時候賦值的,那咱們知道這個i的值不是在,inner(lambda)函數中定義的,而是引用的上層函數的變量,當咱們使用inner去調用的時候,這時這個for循環已經結束了,i的值也就變成固定了9
因此每當咱們經過函數lambda x: x + i 的時候這個i永遠是9
那若是說我不想要這樣的結果,我想要i是循環的值怎麼辦,不要直接引用上層變量,把變量傳進來就能夠了
result = [lambda x, i=i, : x + i for i in range(10)] print(result[1](10))
翻譯一下
def make_list(): L =[] for i in range(10): def inner(arg, i=i): return i + arg L.append(inner) return L result = make_list() print(result[2](10))
簡單解釋下這個概念,在嵌套函數內,嵌套函數應用上層函數的變量(不是全局變量)稱之爲閉包,
def func(): x = 1 def inner(): print(x)
當咱們執行func的時候就會生成一個閉包,func執行完后里面的變量x不會被回收,由於嵌套函數inner還在使用它,常見的應用場景就是咱們的裝飾器