Go語言包管理(一)

Go語言中的包

我們在使用其他語言,比如Java,Python,都有類似包的概念,Go也不例外,其核心思想即爲分組和模塊化。人的大腦對龐大和複雜的事情很難掌控,可以對其採用分而治之的策略,使其模塊化,從而更容易管理。 如下是標準庫中net包的樹形結構圖

net
├─http
│ ├─cgi
│ │ └─testdata
│ ├─cookiejar
│ ├─fcgi
│ ├─httptest
│ ├─httptrace
│ ├─httputil
│ ├─internal
│ ├─pprof
│ └─testdata
├─internal
│ └─socktest
├─mail
├─rpc
│ └─jsonrpc
├─smtp
├─testdata
├─textproto
└─url

庫包和main包

當把一個程序的package聲明爲main的時候(並且該文件包含一個main函數),則表示最終該程序要編譯爲一個可執行的程序。如果程序未聲明爲main,則可以編譯爲一個庫的形式。

包的導入

通過import導入包之後,即可使用包中的變量和函數,如下源碼,我們定義了一個lib包,裏面定義了加,減,乘,除4個方法,然後在另一個包中來使用。

庫中的內容如下:

package lib1

func Add(x, y int) int {
    z := x + y
    return z
}

func Sub(x, y int) int {
    z := x - y
    return z
}

func Mul(x, y int) int {
    z := x * y
    return z
}

func Div(x, y int) int {
    z := x / y
    return z
}

main包的內容如下:

package main

import (
    "fmt"
    "lib1"
)

func main() {
    x := 8
    y := 2

    a := lib1.Add(x, y)
    b := lib1.Sub(x, y)
    c := lib1.Mul(x, y)
    d := lib1.Div(x, y)

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}

輸出:

10
6
16
4

注意庫包中導出的方法若想讓包外可見,則第一個字母要大寫。如Add,Sub… 否則該方法對包外不可見,僅可在本包中使用(變量也有同樣的原則)。Go語言沒有提供類似C++和Java的private,public,protected等關鍵字,也反應了Go語言的哲學思想:簡單實用至上。

包名導入衝突(命名導入)

在使用import導入包的時候,我們就可以通過包名稱引用包中的方法,如上面的lib1,但是如果有又有另一個人開發的包名稱也爲lib1(這裏只包的最後一部分),也就是發生的包命名衝突該如何處理呢。可以在import的名稱前面起一個別名處理此問題,如下分別把“fmt”和“lib1”的包名稱重新命名爲了“fmta”和“liba”

package main

import (
    fmta "fmt"
    liba "lib1"
)

func main() {
    x := 8
    y := 2

    a := liba.Add(x, y)
    b := liba.Sub(x, y)
    c := liba.Mul(x, y)
    d := liba.Div(x, y)

    fmta.Println(a)
    fmta.Println(b)
    fmta.Println(c)
    fmta.Println(d)
}

包的初始化函數 init()

一個包中可能有多個文件,如果我們想對這個包做一些初始化操作該如何做呢?Go提供了一個既沒有參數也沒有返回值的init()函數(類似main函數), 可以在init函數中進行初始化操作。 比如有如下3個包“lib1”,“lib2”“m1”分別代表兩個庫包和一個包含main函數的主包,每個包中有對應的Go文件,包目錄結構如下:

├─lib1 a.go
├─lib2 b.go
└─m1 m.go

“m1”包中的m.go文件會import“lib1”,進一步“lib1”會import“lib2”。且在lib1和lib2中定義了init函數。源碼如下:

//lib2中的a2.go

package lib2

import (
    "fmt"
)

func init() {
    fmt.Println("lib2 init1")
}

func init() {
    fmt.Println("lib2 init2")
}
// lib1中的a1.go
package lib1

import (
    "fmt"
    _ "lib2"
)

func init() {
    fmt.Println("lib1 init1")
}

func init() {
    fmt.Println("lib1 init2")
}
//m1中的m.go
package main

import (
    "fmt"
    _ "lib1"
)

func main() {
    fmt.Println("main")
}

輸出:

lib2 init1
lib2 init2
lib1 init1
lib1 init2
main

可見main最後執行,首先執行的是import各個包時候的init。而且init函數執行的順序是依據依賴順序來執行的。Go會遞歸對依賴進行分析,然後從main包開始分析,首先保證沒有循環依賴。main包發現import了“lib1”,lib1發現又import了“lib2”,因此首先執行lib2的init函數,再返回執行lib1中的init函數,最後返回到main函數來執行。

每個包中可以有多個文件,每個文件中也可以有多個init()函數,但是這些init函數的順序Go是沒有保證的。

包的匿名導入

因爲Go對導入但是未使用的包,會報編譯錯誤。因此可以使用如下的方式。在包名稱之前使用一個下劃線”_”來表示。這樣仍然會執行所import包中的init函數。

package lib1

import (
    "fmt"
    _ "lib2"
)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章