Python【知識點】面試小點列表生成式小坑
1、問題
有這麽一個小面試題:
看下面代碼請回答輸出的結果是什麽?為什麽?
result = [lambda x: x + i for i in range(10)] print(result[0](10))
2、解答
當你看到這篇文章的時候如果不知道這個知識點肯定會拿去直接運行,輸出的結果是什麽呢?
結果是:19
通過result[0~9](10)結果都是19就,懵逼了吧~~
想知道這個我們先看幾個知識點
2.1、列表生成式
顧名思義,列表生成式就是一個用來生成列表的特定語法形式的表達式。
語法格式:
[expression for iterable_var initerable]
工作過程:
- 叠代iterable的每個元素
- 把叠代的值賦值給iterable_var,然後通過expression的表達式進行計算得到一個新的值
- 最後把所有expression的值以新列表的形式返回
工作過程類似於:
L = [] for i in range(10): # 舉例expression 表達式 iterable_var * 5 ,最後把這個結果加到列表中 L.append(i * 5)
點到為止,我們來看下我們的這個小面試題
result = [lambda x: x + i for i inrange(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呢?
2.2、函數內部變量的後期綁定(last binding)
我們在寫一個函數的時候,函數內不保存這個變量的值,而是在執行的時候去找這個值在哪裏綁定上的。
舉個例子來說我們在函數中定引用了一個變量,可以不需要提前定義我們只要在使用前定義就可以了,如下面代碼
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
2.3、小作修改
那如果說我不想要這樣的結果,我想要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))
2.4、閉包
簡單解釋下這個概念,在嵌套函數內,嵌套函數應用上層函數的變量(不是全局變量)稱之為閉包,
def func(): x = 1 def inner(): print(x)
當我們執行func的時候就會生成一個閉包,func執行完後裏面的變量x不會被回收,因為嵌套函數inner還在使用它,常見的應用場景就是我們的裝飾器
Python【知識點】面試小點列表生成式小坑