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/