Python閉包的兩個注意事項

mpnx3600 7年前發布 | 11K 次閱讀 閉包 Python Python開發

延遲綁定

Python閉包函數所引用的外部自由變量是延遲綁定的。

In [2]: def multipliers():
  ...:    return [lambda x: i * x for i in range(4)] 
In [3]: print [m(2) for m in multipliers()]
[6, 6, 6, 6]

如以上代碼: i 是閉包函數引用的外部作用域的自由變量, 只有在內部函數被調用的時候才會搜索變量 i 的值, 由于循環已結束, i 指向最終值3, 所以各函數調用都得到了相同的結果。

解決方法:

1) 生成閉包函數的時候立即綁定( 使用函數形參的默認值 ):

In [5]: def multipliers():
    return [lambda x, i=i: i* x for i in range(4)]
      ...: 
 
In [6]: print [m(2) for m in multipliers()]
[0, 2, 4, 6]

如以上代碼: 生成閉包函數的時候, 可以看到每個閉包函數都有一個帶默認值的參數: i=i , 此時, 解釋器會查找 i 的值, 并將其賦予形參 i , 這樣在生成閉包函數的外部作用域(即外部循環中), 找到了變量 i , 遂將其當前值賦予形參 i 。

2) 使用 functools.partial :

In [26]: def multipliers():
    return [functools.partial(lambda i, x: x * i, i) for i in range(4)]
    ....: 
 
In [27]: print [m(2) for m in multipliers()]
    [0, 2, 4, 6]

如以上代碼: 在有可能因為延遲綁定而出問題的時候, 可以通過 functools.partial 構造偏函數, 使得自由變量優先綁定到閉包函數上。

禁止在閉包函數內對引用的自由變量進行重新綁定

def foo(func):
    free_value = 8
    def _wrapper(*args, **kwargs):
        old_free_value = free_value #保存舊的free_value
        free_value = old_free_value * 2 #模擬產生新的free_value
        func(*args, **kwargs)
        free_value = old_free_value
    return _wrapper

以上代碼會報錯, UnboundLocalError: local variable 'free_value' referenced before assignment , 以上代碼本意是打算實現一個帶有某個初始化狀態( free_value )但在執行內部閉包函數的時候又可以按需變化出新的狀態( free_value = old_free_value * 2 )的裝飾器, 但內部由于發生了重新綁定, 解釋器會將 free_value 看作局部變量, old_free_value = free_value 則會報錯, 因為解釋器認為 free_value 是沒有賦值就被引用了。

解決:打算修改閉包函數引用的自由變量時, 可以將其放入一個list, 這樣, free_value = [8] , free_value 不可修改, 但 free_value[0] 是可以安全的被修改的。

另外, Python 3.x增加了 nonlocal 關鍵字, 也可以解決這個問題。

 

來自:http://python.jobbole.com/87500/

 

 本文由用戶 mpnx3600 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!