前言
請看下面代碼
def multipliers(): return [lambda x : i*x for i in range(4)] print ([m(2) for m in multipliers()] ) """ [6, 6, 6, 6] """
為什么輸出結果為[6, 6, 6, 6],這段代碼相當於
def multipliers(): funcs = [] for i in range(4): def bar(x): return x*i funcs.append(bar) return funcs print ([m(2) for m in multipliers()] ) """ [6, 6, 6, 6] """
解答
運行代碼,解釋器碰到了一個列表解析,循環取multipliers()函數中的值,而multipliers()函數返回的是一個列表對象,這個列表中有4個元素,
每個元素都是一個匿名函數(實際上說是4個匿名函數也不完全准確,其實是4個匿名函數計算后的值,因為后面for i 的循環不光循環了4次,
同時提還提供了i的變量引用,等待4次循環結束后,i指向一個值i=3,這個時候,匿名函數才開始引用i=3,計算結果。所以就會出現[6,6,6,6],
因為匿名函數中的i並不是立即引用后面循環中的i值的,而是在運行嵌套函數的時候,才會查找i的值,這個特性也就是延遲綁定)
# 為了便於理解,你可以想象下multipliers內部是這樣的(這個是偽代碼,並不是准確的): def multipliers(): return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]
因為Python解釋器,遇到lambda(類似於def),只是定義了一個匿名函數對象,並保存在內存中,只有等到調用這個匿名函數的時候,
才會運行內部的表達式,而for i in range(4) 是另外一個表達式,需等待這個表達式運行結束后,才會開始運行lambda 函數,此時的i 指向3,x指向2
改進
def multipliers(): # 添加了一個默認參數i=i return [lambda x, i=i: i*x for i in range(4)] print ([m(2) for m in multipliers()] ) """ [0, 2, 4, 6] """
相當於
def multipliers(): funcs = [] for i in range(4): def bar(x, i=i): return x * i funcs.append(bar) return funcs print ([m(2) for m in multipliers()] ) """ [0, 2, 4, 6] """
解答
添加了一個i=i后,就給匿名函數,添加了一個默認參數,而python函數中的默認參數,
是在python 解釋器遇到def(i=i)或lambda 關鍵字時,就必須初始化默認參數,
此時for i in range(4),每循環一次,匿名函數的默認參數i,就需要找一次i的引用,
i=0時,第一個匿名函數的默認參數值就是0,i=1時,第二個匿名函數的默認參數值就是1,以此類推
# 為了便於理解,你可以想象下multipliers內部是這樣的(這個是偽代碼只是為了理解): def multipliers(): return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3] # x的引用是2 所以output的結果就是:[0,2,4,6]
當然你的i=i,也可以改成a=i。
def multipliers(): # 添加了一個默認參數a=i return [lambda x, a=i: x*a for i in range(4)] print ([m(2) for m in multipliers()] ) """ [0, 2, 4, 6] """
Python的延遲綁定其實就是只有當運行嵌套函數的時候,才會引用外部變量i,不運行的時候,並不是會去找i的值,這個就是第一個函數,為什么輸出的結果是[6,6,6,6]的原因。