Go語言核心之美 1.4-包和檔案

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

一、Package

Go語言中的包(Package)就像其它語言的庫(Library)或模組(Module)一樣,支援模組化,封裝性,可重用性,單獨編譯等特點。包的源碼是由數個.go檔案組成,這些檔案所在的目錄名是import路徑的最後一個詞,例如github.com/sunface/corego包的所有檔案都儲存在$GOPATH/src/github.com/sunface/corego底下。


每個包都有獨立的命名空間。例如,在image包中的Decode和unicode/utf16中的Decode是完全不同的函數。如果要引用第三方庫的函數,我們要使用package.Func的形式,例如image.Decode和utf16.Decode。


包也允許我們自己控制包內變數、函數的可見度。在Go語言中,變數、函數等的匯出只取決於一個因素:名字首字母的大小寫。


想象一下,如果我們的溫度轉換軟體開始流行了,然後希望貢獻給開源社區,應該怎麼做?

首先讓我們建立一個包github.com/sunface/temconv,在1.3節的例子基礎上做一些變化。這個包中包含了兩個檔案,示範了怎麼樣把資料聲明和資料訪問分開,在現實項目中,這個包實際只需要一個檔案。

temconv.go包含了型別宣告,常量,類型的method:

package tempconvimport "fmt"type Celsius float64type Fahrenheit float64const (    AbsoluteZeroC Celsius = -273.15    FreezingC     Celsius = 0    BoilingC      Celsius = 100)func (c Celsius) String() string    { return fmt.Sprintf("%g°C", c) }func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) }

conv.go包含了轉換函式:

//溫度轉換package tempconv// 將Celsius溫度轉換為Fahrenheitfunc CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }// 將Fahrenheit溫度轉換為Celsius.func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

每個.go檔案的第一行都是package tempconv的聲明,表示該檔案屬於哪個包。當包被匯入後,可以這樣調用它的成員:tempconv.CToF等等。包層級的變數,例如類型、常量等,對同一個包內的所有檔案都是可見的,就好像所有代碼定義在同一個檔案中一樣。注意這裡temconv.go匯入了fmt.但是conv.go沒有,因為它沒有用到fmt的任何成員。

上面代碼中包層級的const變數,都是大寫字母開頭的,因此可以在temconv包的外部使用,tempconv.AbsoluteZeroC:

fmt.Println(tempconv.CToF(tempconv.BoilingC)) // "212°F"


我們會選擇包內的某個.go檔案進行包層級的注釋,注釋寫在該檔案的package聲明前(見之前的conv.go)。一般來說,這裡的注釋是對檔案進行概述的。一個包只有一個檔案需要包層級的注釋。這些注釋一般會放在doc.go檔案中,後續可以通過go doc tempconv來查看包注釋。


二、包匯入(import)

在Go程式中,每一個包都是通過一個唯一的字串來標示的,被稱為匯入路徑,這些包是在import聲明中統一匯入的。Go語言規範不會對匯入的包名進行任何約定,這些是由Go的工具來完成解析的。當使用go的工具 (tool)時,一個匯入路徑代表了一個檔案夾,該檔案夾內包含了組成包的.go檔案。

在import聲明中,每個包都有自己的匯入包名,按照慣例,這個包名是匯入路徑的最後一個詞,例如github.com/sunface/tempconv的匯入包名是temconv:

package mainimport (    "fmt"    "os"    "strconv"    "github.com/sunface/tempconv")func main() {    for _, arg := range os.Args[1:] {        t, err := strconv.ParseFloat(arg, 64)        if err != nil {            fmt.Fprintf(os.Stderr, "cf: %v\n", err)            os.Exit(1)        }        f := tempconv.Fahrenheit(t)        c := tempconv.Celsius(t)        fmt.Printf("%s = %s, %s = %s\n",            f, tempconv.FToC(f), c, tempconv.CToF(c))    }}
因此可以直接調用tempconv.CToF。還可以使用別名機制避免包名衝突:
import ("github.com/sunface/tempconv"temp "github.com/sunfei/tempconv")
調用github.com/sunfei/tempconv:temp.CToF;調用github.com/sunface/tempconv:tempconv.CToF。

上面這個程式將單獨的數字命令列參數轉換成Celsius和Fahrenhit的值。

$ go build github.com/sunface/corego/ch1.4/cf$ ./cf 3232°F = 0°C, 32°C = 89.6°F$ ./cf 212212°F = 100°C, 212°C = 413.6°F$ ./cf -40-40°F = -40°C, -40°C = -40°F

如果匯入一個包後不去使用,那麼就會報編譯錯誤,編譯器的這個檢查可以協助消除不需要的包引用,雖然在debug期間可能會比較蛋疼,例如注釋掉log.Print("hello")可能會消除程式對log包的引用,這個時候編譯器就會報錯。還好,我們可以使用golang.org/x/tools/cmd/goimports工具,它會自動插入和移除包引用,大多數ide都支援配置去使用goimports。



三、包的初始化

包的初始化時會按照聲明的順序初始化包層級的變數,除非變數間有依賴順序:

var a = b + c     // a 第三個初始化 3var b = f()       // b 第二個初始化,調用了fvar c = 1         // c 第一個初始化func f() int { return c + 1 }
如果某個包有多個.go檔案,那這些檔案會按照提交給編譯器的順序來初始化,在喚醒編譯器前,go tool會通過檔案名稱對.go檔案進行排序。

任何檔案都可以包含任何數目的init函數:

func init() { /* ... */ }
這種init函數不能被調用也不能被引用,在每個檔案內,init函數都會在程式剛啟動的時候自動運行,檔案內每個init函數會按照聲明的順序依次執行。



每個包只會被初始化一次,首先是初始化依賴包,如果包p匯入q,可以肯定的是:q在p初始化之前肯定會完全初始化。因為依賴包會先初始化,所以程式的初始化是自底向上的,main包肯定是最後一個初始化(包的組織類似一個樹形結構,main是根節點)。這樣在main函數開始時,所有的包都會初始化完畢!




文章所有權:Golang隱修會 連絡人:孫飛,CTO@188.com!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.