認真一點學 Go:11. 包

printlove發表於2021-10-08

最新文章集合

學到什麼

  1. 什麼是包?

  2. 如果宣告包?

  3. 如何匯入包?

  4. 原始檔的組成部分?

  5. 包內容如何公開和私有?

  6. main 包的作用?

  7. internal 目錄的作用?

  8. 多個包出現匯入時,之間的載入順序是什麼?

概念

做個類比理解下包是啥?當電腦上檔案變多時,就會通過目錄區分,將不同的檔案有組織的歸類在不同的目錄下。Go 原始檔也是一樣,可以把不同的檔案放置在不同的目錄中,給目錄取一個別名,就是所說的包名。

下來學習包的使用,就是熟悉 Go 專案中程式碼的組織結構,為了直觀,直接看專案目錄。在上手前首先要掌握 go mod 的使用,不熟悉請前往《環境搭建 - gomod疑惑》

專案目錄

建立一個 gobasic 的專案目錄,總共包含三個部分:入口檔案、gomod、自定義兩個包。

認真一點學 Go:11. 包

呼叫順序:main.go >> b.go >> a.go

下來從這三個檔案入手,開始學習。

包宣告

在原始檔的開頭新增如下程式碼格式:


// a.go

package pkgA

pkgA 為自定義的包名。標準規範中,該命名與原始檔所在目錄名稱相同。入口 “main 函式” 的所在原始檔包名必須設定為 main。

在同一個包(目錄)下,可以建立多個原始檔。

包匯入

宣告一個包後,就可以被其它包匯入使用,格式如下:


// b.go

package pkgB

import "github.com/miaogaolin/gobasic/pkgA"

檔案開頭宣告瞭原始檔所在包為 pkgB ,下來使用 import 關鍵字匯入所依賴的包。如果匯入的是當前專案中的包,引用路徑的規則為 “go.mod” 檔案中設定的 module 值與依賴包的目錄路徑拼接。

我的專案 module 值為:

github.com/miaogaolin/gobasic

匯入之後,就可以使用“包名+點”訪問包內的變數、常量、函式、結構體、介面。

1. 多包匯入

如果有多個包需要匯入時,有兩種方式,第二種為常見方式。


// 第一種

import "gobasic/pkgA"

import "fmt"

// 第二種

import (

   "fmt"

   "github.com/miaogaolin/gobasic/pkgA"

)

引用後就可以呼叫包內的函式、常量、結構體等等。如果呼叫的函式在同一個包下,就不需要匯入,可以直接呼叫。

2. 簡化匯入

使用“點”匯入的包,在呼叫包內的變數、常量、函式等等,就不需要寫包名。

例:


import . "fmt"

// 不使用“點”匯入

fmt.Println()

// 使用“點”匯入

Println()

3. 別名匯入

給匯入的包可以使用一個別名,這樣如果匯入的多個包時,名稱一樣出現衝突時,就可以取個別名。

例:


import a "exmaple/pkgA"

// a 為別名

a.Func1()

4. 匿名匯入

在匯入包時,如果該包沒有被使用,那編譯器就會報錯。為了不讓報錯,可以使用匿名匯入。那為何不直接刪除呢?是因為想使用包內的 init() 函式,該函式在包被匯入時自動呼叫。

例:


import _ "github.com/go-sql-driver/mysql"

例子中,mysql 這個包內會存在一個 ”init 函式“,該函式的意義表示註冊 mysql 驅動。

原始檔組成

瞭解了包的宣告和引用後,下來看看原始檔的完整組成結構。

認真一點學 Go:11. 包

例:


// b.go

package pkgB

import (

   "fmt"

   "github.com/miaogaolin/gobasic/pkgA"

)

var name string

func init() {

   name = "B"

}

func PrintName() {

   fmt.Println(name)

    // 呼叫 pkgA 包中的一個函式

   pkgA.PrintName()

}

在這個例子中沒有給出 pkgA 包的程式碼,完整的程式碼前往:

github.com/miaogaolin/gobasic

下來根據這個例子講解兩個知識點,繼續往下看。

1. 包的使用

pkgA.PrintName()這個訪問有個前提,就是函式的命名首字母必須大寫,如果是小寫開頭那隻能在當前包內訪問,而不能被其它包呼叫。

例如, b.go 檔案中的 name 變數就不能被其它包訪問。如果想要訪問,就需要改為 Name 。函式、結構體、介面、常量,也是一樣的。

總結下就是小寫字母開頭私有,大寫字母開頭公有

2. init 函式

該函式是 Go 語言中的保留函式,當包被匯入後自動執行,不需要主動呼叫。該函式可以在同一個包內的不同原始檔中使用。如果是自定義的函式自然是不行的,相同的函式名稱只能在同一個包內出現一次。

main 包

在學習 Go 語言開始時,寫了一個 “Hello World” 輸出的例子中就有見過,摘取關鍵程式碼如下:


// 入口檔案的包名

package main

// 入口函式

func main() {

   ...   

}

“main 包”和“main 函式” 這兩者的組合,確定了程式的啟動入口。

internal 目錄

這也是 Go 語言中一個特殊的目錄,如果原始檔在 internal 目錄中,那該目錄的父級父級目錄是不能訪問 internal 目錄下的內容的。

這塊我說的是目錄,不是包名。雖然我在前面講了,在規範中,目錄名稱和包名保持相同,但如果不相同語法也是正確的。

回到 internal 目錄,如果 internal 目錄下定義的包名不是 internal 名稱,外部也是不能訪問的。只要目錄名稱不是 internal 就算包名是,外部就能訪問。

舉例展示一個 internal 目錄層級,說明下訪問性。

  • exmaple
    • dir1
      • internal
    • main.go

在上例中,internal 目錄的父級父級目錄是 exmaple,那在 main.go 檔案中就不能訪問 internal 目錄中的內容。

包的載入順序

這裡我們從 main 包入口開始,main 包匯入 pkg1,pkg1 匯入 pkg2,pkg2 匯入 pkg3。

認真一點學 Go:11. 包

過程如下:

  1. main 包執行 import pkg1。

  2. pkg1 繼續執行 import pkg2。

  3. pkg2 繼續執行 import pkg3。

  4. pkg3 由於沒有依賴其它包,所以向下執行常量、變數的初始化並執行 init 函式。

  5. 接下來執行 pkg2、pkg1、main 包內的常量、變數初始化和執行 init 函式。

  6. 最後開始執行 main 函式。

總結

熟悉了包的應用後,下來就要多熟悉熟悉常用包的使用,比如:

  • fmt

  • time

  • strings

  • regexp

等等…

當然也不是都要熟悉,等基礎知識學完後,找個實際專案實戰,看到哪個包就學哪個就夠了。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章