iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 22
0

今天要來談談 Generic。由於靜態語言對於宣告的類型有嚴格的規定,Generic 的出現則是讓這件事情變得更加彈性,可以將一套程式碼複用在不同類型的資料上。例如所謂 Generic class 或 Generic function 的參數就會除了一般的參數之外,也會有參數代表的是類型,也就是類型參數化。這樣子的好處是,就不用只是因為參數類型不同,就需要再撰寫一個很像的函式,例如把整數跟字串印出來的函數,可能邏輯上是完全一樣的,就不用特別寫像是 printIntprintStr。待會我們就會來看一些例子囉!


Python 3

  • 我們在一開始提到說,Generic 是靜態語言才有這樣的需求,然而在 Python 的世界,並沒有辦法去限制參數是什麼樣的種類,所以理論上也就沒有 Generic 的存在必要。
  • 然而在 Python 3.5 之後引入了類型系統,方便開發並供 IDE 和其他工具使用,此後有了一個標準可以在程式碼中去添加類型的標示,但執行時會被忽略。這樣的類型系統雖然實際上沒有作用,但是有帶來一些好處,例如增加可讀性,藉由標注參數以及返回值得類型,能夠更方便快速理解。另一方面 IDE 透過瞭解類型,也可以更精準提示類型的方法與屬性,甚至可以在錯誤使用類型的時候給予警告。Python 提供了一個叫做 typing 的 Library 來提供像是 ListTupleDict 等等 Type hints 供我們使用。例如下面的程式碼,我們從 typing 引入了 Dict, Tuple, List,而 OptionsHostServer 是 Type alias,可以讓我們自定義類型的別名以供後續使用,所以 connect 的參數 servers 就可以寫成 List[Server]-> None 表示返回的是 None
from typing import Dict, Tuple, List

Options = Dict[str, str]
Host = Tuple[str, int]
Server = Tuple[Host, Options]

def connect(servers: List[Server]) -> None:
    pass
  • 既然引入了類型系統,所以當然也有 Generic 可以讓我們拿來標注囉!T = TypeVar('T') 是定義了類型參數,而 Generic[T] 就是表示泛型,因此我們就可以在方法裡頭去標註類型參數了!
T = TypeVar('T') 
class LoggedVar(Generic[T]): 
    def set(self, new: T) -> None: 
        pass
    def get(self) -> T:
        pass
  • 對於 typing 有興趣的可以參考這裡囉!

Golang

  • Golang 雖然在像 Array、Slice、Map 等容器結構支援 Generic,但也僅止於此,並沒有辦法像其他靜態語言可以定義類型參數,因為官方認為這樣會增加設計上的複雜度 (據說 Golang 2.0 會支援,拭目以待!)。
  • 目前看來在 Golang 的方式是以 Interface 來作為 Generic 的替代,例如某函式的參數類型是某 Interface,只要自定義的類型有實作此 Interface,就可以當作這個函式的參數傳入,達到某種程度的泛型。不過麻煩的就是每個類型都必須要有實作該 Interface 的函式們,顯得較為累贅。倘若我們今天要讓某個函式可以接收所有的類型,那我們就可以在定義的時候使用空的 Interface,所謂空的意思是這個 Interface 沒有任何方法簽名,所以任何類型都是這個 Interface。例如下面的 v interface{} 就是代表 v 可以是任何類型,而回傳也是任何類型。至於如何在程式裡頭去知道是什麼類型並做對應的事情呢?這裡用的是 Type switch,switch v.(type) 會去看傳入的 v 是不是 case 中的 Type,是的話就執行對應的程式。而 v.(int)v.(float64) 是 Type assertion,例如 v.(int) 表示如果 v 是 int 就會得到真正這個 int 的值,假使不是 int 就會引發 panic。然而我們也可以這樣使用 i, ok := v.(int) 這樣就不會 panic,而是把是否是 int 這件事情存在 ok 之中囉!
package main
import (
   "reflect"
)
func Test(v interface{}) interface{} {
    switch v.(type) {
        case int:
            return v.(int) + 10
        case float64:
            return v.(float64) + 22.3
    }
    return v
}

上一篇
[Day 20] 把東西給我排好
下一篇
[Day 22] 種下一棵有用的樹
系列文
30 天把自己榨好榨滿的四週四語言大挑戰!30

尚未有邦友留言

立即登入留言