Python@TW: 會眾| IRC| Planet| 郵件列表| 聯絡我們

closure 是什麼?避免讓你寫 function object 。寫過 Java 程式的人,應該都知道 function object ,用以傳遞 caller 所選擇的演算法或公式, callee 在適當的時間呼叫。使用 C 語言時,其實你只需傳遞一個 function pointer 就可以達成要求,但 Java 病態的要求 OO ,所以有 function object 這樣的 pattern 。

有時,你的 function 必需依據某組數據運算,資料必需隨著 function 一起傳遞,因此就算是 C ,也必需採用 function object 的方式,將資料和演算法一同傳遞。於是,明明你要傳遞的是 function ,即使是短短一兩行,卻必需包裝成 object ,只因為 function 會參考到某些資料。這會造成程式碼過度的分散,明明這一兩行程式碼只和前後幾行相關,卻必需包裝成 function object ,而必需移到其它較遠的地方,而非出現在該出現的地方。這造成閱讀上的不直覺和不連續,降低程式碼的閱讀性。

closure 能用在巢狀宣告 function 或 lambda ,讓 nested function 和 lambda 能直接參考包裹該者的 function 所定義之 local 變數。其中的秘密,在於 closure 所參考到的外層變數,並不會因為離開該外層 function 而釋放。而 closure 被傳遞到其它 function ,並不會因為在不同的 scope 呼叫,而引用該 scope 的變數,而非原來的 scope 。 在定義該 closure function 的程式碼每一次被執行的同時,也建立了一個 closure object 。在 closure object 被建立的同時,closure 所引用到的外層變數就已經確立了。因此, closure 所引用的變數,並不會因呼叫的位置改變而改變。永遠是引用 closure object 建立的那一瞬間,所看到的外層變數。

因為這樣的特性, programmer 可以直接巢狀寫一個 function 或 lambda , 並傳遞給其它的 object 或 function ,而不需包裝成 function object 。說這麼多觀念上的東西,不如直接來個實例:

   1 def create_classifier(delimits):
   2     def classifier(data):
   3         for i, delimit in enumerate(delimits):
   4             if data <= delimit: return i
   5         return len(delimits)
   6     return classifier
   7  
   8 
   9 def count_by_group(items, classifier):
  10     counters  = {}
  11     for item in items:
  12         group = classifier(item)
  13         counters[group] = counters.setdefault(clazz, 0) + 1
  14     return counters
  15 
  16 classifier = create_classifier([3, 15, 24, 39, 100])
  17 counters = count_by_group([1, 1000, 50, 37, 77, .....], classifier)
  18 print counters

這個實例, count_by_group() 將輸入的資料分類,並計算每一類的數量。而 create_classifier() 則建立分類的演算法,依據一組數,以區間分類。本例指定了 [3, 15, 24, 39, 100] 的區間,依序分類為 0 、 1 、 2 、 3 、 4 ,而超過 100 者,為分類 5 。

closure 除了上例,用以傳遞演算法外,還可用來延遲運算。例如,你有一組資料必需運算,但運算必需花費很多 CPU time 。於是必需等到需要用到這組數據時,才開始正真的運算,避免進行無用的運算。這時,就能利用 closure ,將運算的方法和資料一起傳遞。等到真正需要運算結果時,才去呼叫 closure ,產生結果。

closure 並不是必需的設計,但使用 closure 能增加程式的閱讀性,避免程式碼分散。

參考

-- Thinker


CategoryCookbook

Python/Cookbook/Closure (上次是 211 在 2016-02-19 16:27:58 編輯的)