Python函數——閉包延遲綁定


前言

請看下面代碼

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]的原因。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM