談到closure,首先要先瞭解First-cass Function,所謂的first-class function是指函式可以被assign給變數或儲存於資料結構中,並且可以作為其他函數的參數,或是作為其他的函數的回傳值。而python裡的每個function都是first-class function,所以在python中可以作到以下的操作:
1 | def max(m, n): |
我們可以把max這個函式assign給func,再使用func來實際使用max這個函式。
要建立一個closure,在python中可以透過建立巢狀函式(nested function)來實作,其中內部函式會依據LEGB規則參照Enclosed scope的外部函式變數,此變數又稱為自由變數(free variable)。自由變數將會和此函式同時存在,即使離開了創造此變數的環境/區域也可以被此函式使用不會消失。
1 | def target_value(x): |
上面是一個closure的例子,首先呼叫外部函式target_value將參數傳入,並回傳內部函式compare且assign給func,這是一個first-class function的實作。而每一次呼叫target_value後就會產生一個closure實例,每一個實例會binding到不同的自由變數x,它並不會因為離開了創造它的target_value函式而消失,反而在透過func呼叫compare時被closure補捉使用。python還有個__colsure__可以看到在enclosed scop所補捉到的自由變數
總結來說,並不是每個巢狀函式都是closure,巢狀函式需要補捉到enclosed scope的自由變數,使得此變數在脫離原本的環境仍可被內部函式使用,這樣才為closure。
在使用上closure並不是一定要有的設計方式,但是有時使用closure可以幫助有效的組織程式碼,並提升程式碼的可讀性。像是當我們有資料需要重複使用,但是又不想都塞在global,這時可以透過自由變數來存放再用closure捕捉來降低global變數的數量;另外也可以透過closure來隱藏變數,作出private的效果。
2016/11/14
捕捉到自由變數並不能作修改,如果需要修改自由變數,可透過nonlocal來宣告變數,並作進一步的操作
參考資料:
Wiki-First-Class Function
Wiki-Closure
認識 Lambda/Closure(3)Python 對 Lambda/Closure 的支援