691
技術社區[雲棲]
Go語言與數據庫開發:01-09
包和工具
Go語言有超過100個的標準包(譯注:可以用 go list std | wc -l 命令查看標準包的具體數
目),標準庫為大多數的程序提供了必要的基礎構件。在Go的社區,有很多成熟的包被設
計、共享、重用和改進,目前互聯網上已經發布了非常多的Go語音開源包,它們可以通過
https://godoc.org 檢索。
Go還自帶了工具箱,裏麵有很多用來簡化工作區和包管理的小工具。
包簡介
任何包係統設計的目的都是為了簡化大型程序的設計和維護工作,通過將一組相關的特性放
進一個獨立的單元以便於理解和更新,在每個單元更新的同時保持和程序中其它單元的相對
獨立性。這種模塊化的特性允許每個包可以被其它的不同項目共享和重用,在項目範圍內、
甚至全球範圍統一的分發和複用。
每個包一般都定義了一個不同的名字空間用於它內部的每個標識符的訪問。每個名字空間關
聯到一個特定的包,讓我們給類型、函數等選擇簡短明了的名字,這樣可以避免在我們使用
它們的時候減少和其它部分名字的衝突。
每個包還通過控製包內名字的可見性和是否導出來實現封裝特性。通過限製包成員的可見性
並隱藏包API的具體實現,將允許包的維護者在不影響外部包用戶的前提下調整包的內部實
現。通過限製包內變量的可見性,還可以強製用戶通過某些特定函數來訪問和更新內部變
量,這樣可以保證內部變量的一致性和並發時的互斥約束。
當我們修改了一個源文件,我們必須重新編譯該源文件對應的包和所有依賴該包的其他包。
即使是從頭構建,Go語言編譯器的編譯速度也明顯快於其它編譯語言。Go語言的閃電般的編
譯速度主要得益於三個語言特性。第一點,所有導入的包必須在每個文件的開頭顯式聲明,
這樣的話編譯器就沒有必要讀取和分析整個源文件來判斷包的依賴關係。第二點,禁止包的
環狀依賴,因為沒有循環依賴,包的依賴關係形成一個有向無環圖,每個包可以被獨立編
譯,而且很可能是被並發編譯。第三點,編譯後包的目標文件不僅僅記錄包本身的導出信
息,目標文件同時還記錄了包的依賴關係。因此,在編譯一個包的時候,編譯器隻需要讀取
每個直接導入包的目標文件,而不需要遍曆所有依賴的的文件
導入路徑
每個包是由一個全局唯一的字符串所標識的導入路徑定位。出現在import語句中的導入路徑也
是字符串。
import (
"fmt"
"math/rand"
"encoding/json"
"golang.org/x/net/html"
"github.com/go-sql-driver/mysql"
)
Go語言的規範並沒有指明包的導入路徑字符串的具體含義,導
入路徑的具體含義是由構建工具來解釋的。
也有第三方擴展的工具箱存在。例如,Google
公司內部的Go語言碼農,他們就使用內部的多語言構建係統。用不同的規則來處理包名字和定位包,
用不同的規則來處理單元測試等等,因為這樣可以更緊密適配他們內部環境。
如果你計劃分享或發布包,那麼導入路徑最好是全球唯一的。為了避免衝突,所有非標準庫
包的導入路徑建議以所在組織的互聯網域名為前綴;而且這樣也有利於包的檢索。例如,上
麵的import語句導入了Go團隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動。
包聲明
在每個Go語音源文件的開頭都必須有包聲明語句。包聲明語句的主要目的是確定當前包被其
它包導入時默認的標識符。
例如,math/rand包的每個源文件的開頭都包含 package rand 包聲明語句,所以當你導入這個
包,你就可以用rand.Int、rand.Float64類似的方式訪問包的成員。
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println(rand.Int())
}
通常來說,默認的包名就是包導入路徑名的最後一段,因此即使兩個包的導入路徑不同,它
們依然可能有一個相同的包名。例如,math/rand包和crypto/rand包的包名都是rand。
關於默認包名一般采用導入路徑名的最後一段的約定也有三種例外情況。第一個例外,包對
應一個可執行程序,也就是main包,這時候main包本身的導入路徑是無關緊要的。
名字為main的包是給go build構建命令一個信息,這個包編譯完之後必須調用連接器生
成一個可執行程序。
第二個例外,包所在的目錄中可能有一些文件名是以test.go為後綴的Go源文件,並且這些源文件聲明的包名也是以
_test為後綴名的。這種目錄可以包含兩種包:一種普通包,加一種則是測試的外部擴展包。
所有以_test為後綴包名的測試外部擴展包都由go test命令獨立編譯,普通包和測試的外部擴
展包是相互獨立的。
第三個例外,一些依賴版本號的管理工具會在導入路徑後追加版本號信息
導入聲明
可以在一個Go語言源文件包聲明語句之後,其它非導入聲明語句之前,包含零到多個導入包
聲明語句。每個導入聲明可以單獨指定一個導入路徑,也可以通過圓括號同時導入多個導入
路徑。
下麵兩個導入形式是等價的,但是第二種形式更為常見。
import "fmt"
import "os"
import (
"fmt"
"os"
)
導入的包之間可以通過添加空行來分組;通常將來自不同組織的包獨自分組。包的導入順序
無關緊要,但是在每個分組中一般會根據字符串順序排列。
import (
"fmt"
"html/template"
"os"
"golang.org/x/net/html"
"golang.org/x/net/ipv4"
)
如果我們想同時導入兩個有著名字相同的包,例如math/rand包和crypto/rand包,那麼導入聲
明必須至少為一個同名包指定一個新的包名以避免衝突。這叫做導入包的重命名。
import (
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)
導入包的重命名隻影響當前的源文件。其它的源文件如果導入了相同的包,可以用導入包原
本默認的名字或重命名為另一個完全不同的名字。
導入包重命名是一個有用的特性,它不僅僅隻是為了解決名字衝突。如果導入的一個包名很
笨重,特別是在一些自動生成的代碼中,這時候用一個簡短名稱會更方便。選擇用簡短名稱
重命名導入包時候最好統一,以避免包名混亂。選擇另一個包名稱還可以幫助避免和本地普
通變量名產生衝突。例如,如果文件中已經有了一個名為path的變量,那麼我們可以
將"path"標準包重命名為pathpkg。
每個導入聲明語句都明確指定了當前包和被導入包之間的依賴關係。如果遇到包循環導入的
情況,Go語言的構建工具將報告錯誤。
最後更新:2017-11-12 18:04:58