0%

Closure的基本概念

談到closure,首先要先瞭解First-cass Function,所謂的first-class function是指函式可以被assign給變數或儲存於資料結構中,並且可以作為其他函數的參數,或是作為其他的函數的回傳值。而python裡的每個function都是first-class function,所以在python中可以作到以下的操作:

1
2
3
4
5
6
7
8
def max(m, n):    
return m if m > n else n

>>> max(5, 3)
5
>>> func = max
>>> func(7, 5)
7

我們可以把max這個函式assign給func,再使用func來實際使用max這個函式。

要建立一個closure,在python中可以透過建立巢狀函式(nested function)來實作,其中內部函式會依據LEGB規則參照Enclosed scope的外部函式變數,此變數又稱為自由變數(free variable)。自由變數將會和此函式同時存在,即使離開了創造此變數的環境/區域也可以被此函式使用不會消失。

1
2
3
4
5
6
7
8
9
10
11
12
13
def target_value(x):    
def compare(y):
return '> target' if y > x else '< target'
return compare

>>> func = target_value(10)
>>> func(5)
< target
>>> func2 = target_value(3)
>>> func2(5)
> target
>>> func.__closure__[0].cell_contents
10

上面是一個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 的支援