golang之pkg(包)
一、概述
Golang擁有超過100個標(biāo)準(zhǔn)包(可用go list std |wc -l查看)
任何包系統(tǒng)設(shè)計的目的都是簡化大型程序的設(shè)計和維護(hù)工作,通過將一組相關(guān)的特性放進(jìn)一個獨立的模塊以便于理解和更新,在每個模塊更新的同時保持和程序中其他模塊的相對獨立,這種模塊化的特性允許每個包能被其他的不同項目共享和重用,在項目范圍內(nèi)、全局范圍內(nèi)的復(fù)用;
每個包一般都定義了一個不同的名字空間用于它內(nèi)部的每個標(biāo)識符的訪問。 每個名字空間關(guān)聯(lián)到一個特定的包, 讓我們給類型、 函數(shù)等選擇簡短明了的名字, 這樣可以避免在我們使用它們的時候減少和其它部分名字的沖突。
每個包還通過控制包內(nèi)名字的可見性和是否導(dǎo)出來實現(xiàn)封裝特性。 通過限制包成員的可見性并隱藏包API的具體實現(xiàn), 將允許包的維護(hù)者在不影響外部包用戶的前提下調(diào)整包的內(nèi)部實現(xiàn)。 通過限制包內(nèi)變量的可見性, 還可以強制用戶通過某些特定函數(shù)來訪問和更新內(nèi)部變量, 這樣可以保證
內(nèi)部變量的一致性和并發(fā)時的互斥約束。
二、包的使用之導(dǎo)入路徑
每個包是由一個全局唯一的字符串所標(biāo)識的導(dǎo)入路徑定位。 出現(xiàn)在import語句中的導(dǎo)入路徑也是字符串。
import (
"fmt"
"math/rand"
"encoding/json"
"golang.org/x/net/html"
"github.com/go-sql-driver/mysql"
)
三、包聲明:
在每個Go源文件的開頭都必須有包聲明語句。 包聲明語句的主要目的是確定當(dāng)前包被其它包導(dǎo)入時默認(rèn)的標(biāo)識符(也稱為包名)
例如, math/rand包的每個源文件的開頭都包含 package rand 包聲明語句, 所以當(dāng)你導(dǎo)入這個包, 你就可以用rand.Int、 rand.Float64類似的方式訪問包的成員。
關(guān)于默認(rèn)包名一般采用導(dǎo)入路徑名的最后一段的約定也有三種例外情況。
第一個例外, 包對應(yīng)一個可執(zhí)行程序, 也就是main包, 這時候main包本身的導(dǎo)入路徑是無關(guān)緊要的。 名字為main的包是給go build( §10.7.3) 構(gòu)建命令一個信息, 這個包編譯完之后必須調(diào)用連接器生成一個可執(zhí)行程序。
第二個例外, 包所在的目錄中可能有一些文件名是以test.go為后綴的Go源文件( 譯注:前面必須有其它的字符, 因為以``前綴的源文件是被忽略的) , 并且這些源文件聲明的包名也是以_test為后綴名的。 這種目錄可以包含兩種包:一種普通包, 加一種則是測試的外部擴展包。
所有以_test為后綴包名的測試外部擴展包都由go test命令獨立編譯, 普通包和測試的外部擴展包是相互獨立的。 測試的外部擴展包一般用來避免測試代碼中的循環(huán)導(dǎo)入依賴。
第三個例外, 一些依賴版本號的管理工具會在導(dǎo)入路徑后追加版本號信息, 例如"gopkg.in/yaml.v2"。 這種情況下包的名字并不包含版本號后綴, 而是yaml?! ?/p>
warning:如果我們想同時導(dǎo)入兩個有著名字相同的包, 例如math/rand包和crypto/rand包, 那么導(dǎo)入聲明必須至少為一個同名包指定一個新的包名以避免沖突。 這叫做導(dǎo)入包的重命名。
import (
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)
warning:匿名導(dǎo)入:
import _ "image/png" // register PNG decoder
1 // The jpeg command reads a PNG image from the standard input 2 // and writes it as a JPEG image to the standard output. 3 package main 4 import ( 5 "fmt" 6 "image" 7 "image/jpeg" 8 _ "image/png" // register PNG decoder 9 "io" 10 "os" 11 )
12 func main() { 13 if err := toJPEG(os.Stdin, os.Stdout); err != nil { 14 fmt.Fprintf(os.Stderr, "jpeg: %v ", err) 15 os.Exit(1) 16 } 17 } 18 func toJPEG(in io.Reader, out io.Writer) error { 19 img, kind, err := image.Decode(in) //這里可能需要匿名導(dǎo)入 20 if err != nil { 21 return err 22 } 23 fmt.Fprintln(os.Stderr, "Input format =", kind) 24 return jpeg.Encode(out, img, &jpeg.Options{Quality: 95}) 25 }
下面說說工作機制。 標(biāo)準(zhǔn)庫還提供了GIF、 PNG和JPEG等格式圖像的解碼器,用戶也可以提供自己的解碼器, 但是為了保持程序體積較小, 很多解碼器并沒有被全部包含, 除非是明確需要支持的格式。 image.Decode函數(shù)在解碼時會依次查詢支持的格式列表。
每個格式驅(qū)動列表的每個入口指定了四件事情:格式的名稱;一個用于描述這種圖像數(shù)據(jù)開頭部分模式的字符串, 用于解碼器檢測識別;一個Decode函數(shù)用于完成解碼圖像工作;一個DecodeConfig函數(shù)用于解碼圖像的大小和顏色空間的信息。 每個驅(qū)動入口是通過調(diào)用image.RegisterFormat函數(shù)注冊, 一般是在每個格式包的init初始化函數(shù)中調(diào)用, 例如image/png包是這樣注冊的:
1 package png // image/png
2 func Decode(r io.Reader) (image.Image, error)
3 func DecodeConfig(r io.Reader) (image.Config, error)
4 func init() {
5 const pngHeader = "x89PNG
x1a
"
6 image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
7 }
最終的效果是, 主程序只需要匿名導(dǎo)入特定圖像驅(qū)動包就可以用image.Decode解碼對應(yīng)格式的圖像!
四、自定義自己的包:
包是函數(shù)和數(shù)據(jù)的集合。用 package 關(guān)鍵字定義一個包。文件名不需要與包名一致,但是你的文件夾必須要與包名字一致,這個很重要,所以為了簡單起見,名字都是一樣的。包名的約定是使用小寫字符。Go 包可以由多個文件組成,但是使用相同的;
package 這一行。讓我們在文件 test1.go 中定義一個叫做 pkg的包。
1 ├── pkg
2 │ └── myprint.go
3
4 //myprint.go
5 package pkg
6 import "fmt"
7 func MyPrintf(in string) {
8 fmt.Printf("%s", in)
9 }
在main包中測試:
1 package main
2 import (
3 "fmt"
4 "./pkg" //帶上路徑
5 )
6
7 func main() {
8 pkg.MyPrinf("my pkg test")
9 fmt.Printf("test ok")
10 }
11
12 //測試ok
13 稍作修改:
14 package main
15 import (
16 "fmt"
17 "pkg" //不帶上路徑
18 )
19
20 func main() {
21 pkg.MyPrinf("my pkg test")
22 fmt.Printf("test ok")
23 }
24
25 //closure.go:6:2: cannot find package "pkg" in any of:
26 /root/golang/go/src/pkg (from $GOROOT)
27 /root/src/pkg (from $GOPATH)
28
29 # 解決辦法:將pkg包加個軟連接到$GOROOT 或$GOPATH
總結(jié)
以上是生活随笔為你收集整理的golang之pkg(包)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php nginx配置404页面,Ngi
- 下一篇: 不同MCU的大小端(endian)分类