日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《Go语言圣经》学习笔记 第十章 包和工具

發(fā)布時(shí)間:2024/4/11 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Go语言圣经》学习笔记 第十章 包和工具 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

《Go語言圣經(jīng)》學(xué)習(xí)筆記 第十章 包和工具


目錄

  • 包簡(jiǎn)介
  • 導(dǎo)入路徑
  • 包聲明
  • 導(dǎo)入聲明
  • 包的匿名導(dǎo)入
  • 包和命名
  • 工具
  • 注:學(xué)習(xí)《Go語言圣經(jīng)》筆記,PDF點(diǎn)擊下載,建議看書。
    Go語言小白學(xué)習(xí)筆記,書上的內(nèi)容照搬,大佬看了勿噴,以后熟悉了會(huì)總結(jié)成自己的讀書筆記。


  • 現(xiàn)在隨便一個(gè)小程序的實(shí)現(xiàn)都可能包含超過10000個(gè)函數(shù)。 然而作者一般只需要考慮其中很小的一部分和做很少的設(shè)計(jì), 因?yàn)榻^大部分代碼都是由他人編寫的, 它們通過類似包或模塊的方式被重用。
  • Go語言有超過100個(gè)的標(biāo)準(zhǔn)包( 譯注: 可以用 go list std | wc -l 命令查看標(biāo)準(zhǔn)包的具體數(shù)目) , 標(biāo)準(zhǔn)庫(kù)為大多數(shù)的程序提供了必要的基礎(chǔ)構(gòu)件。 在Go的社區(qū), 有很多成熟的包被設(shè)計(jì)、 共享、 重用和改進(jìn), 目前互聯(lián)網(wǎng)上已經(jīng)發(fā)布了非常多的Go語音開源包, 它們可以通過http://godoc.org 檢索。 在本章, 我們將演示如果使用已有的包和創(chuàng)建新的包。
  • Go還自帶了工具箱, 里面有很多用來簡(jiǎn)化工作區(qū)和包管理的小工具。 在本書開始的時(shí)候, 我們已經(jīng)見識(shí)過如何使用工具箱自帶的工具來下載、 構(gòu)件和運(yùn)行我們的演示程序了。 在本章,我們將看看這些工具的基本設(shè)計(jì)理論和嘗試更多的功能, 例如打印工作區(qū)中包的文檔和查詢相關(guān)的元數(shù)據(jù)等。 在下一章, 我們將探討探索包的單元測(cè)試用法。

  • 1. 包簡(jiǎn)介

  • 任何包系統(tǒng)設(shè)計(jì)的目的都是為了簡(jiǎn)化大型程序的設(shè)計(jì)和維護(hù)工作, 通過將一組相關(guān)的特性放進(jìn)一個(gè)獨(dú)立的單元以便于理解和更新, 在每個(gè)單元更新的同時(shí)保持和程序中其它單元的相對(duì)獨(dú)立性。 這種模塊化的特性允許每個(gè)包可以被其它的不同項(xiàng)目共享和重用, 在項(xiàng)目范圍內(nèi)、甚至全球范圍統(tǒng)一的分發(fā)和復(fù)用。
  • 每個(gè)包一般都定義了一個(gè)不同的名字空間用于它內(nèi)部的每個(gè)標(biāo)識(shí)符的訪問。 每個(gè)名字空間關(guān)聯(lián)到一個(gè)特定的包, 讓我們給類型、 函數(shù)等選擇簡(jiǎn)短明了的名字, 這樣可以避免在我們使用它們的時(shí)候減少和其它部分名字的沖突。每個(gè)包還通過控制包內(nèi)名字的可見性和是否導(dǎo)出來實(shí)現(xiàn)封裝特性。 通過限制包成員的可見性并隱藏包API的具體實(shí)現(xiàn), 將允許包的維護(hù)者在不影響外部包用戶的前提下調(diào)整包的內(nèi)部實(shí)現(xiàn)。 通過限制包內(nèi)變量的可見性, 還可以強(qiáng)制用戶通過某些特定函數(shù)來訪問和更新內(nèi)部變量, 這樣可以保證內(nèi)部變量的一致性和并發(fā)時(shí)的互斥約束。
  • 當(dāng)我們修改了一個(gè)源文件, 我們必須重新編譯該源文件對(duì)應(yīng)的包和所有依賴該包的其他包。即使是從頭構(gòu)建, Go語言編譯器的編譯速度也明顯快于其它編譯語言。 Go語言的閃電般的編譯速度主要得益于三個(gè)語言特性。 第一點(diǎn), 所有導(dǎo)入的包必須在每個(gè)文件的開頭顯式聲明,這樣的話編譯器就沒有必要讀取和分析整個(gè)源文件來判斷包的依賴關(guān)系。 第二點(diǎn), 禁止包的環(huán)狀依賴, 因?yàn)闆]有循環(huán)依賴, 包的依賴關(guān)系形成一個(gè)有向無環(huán)圖, 每個(gè)包可以被獨(dú)立編譯, 而且很可能是被并發(fā)編譯。 第三點(diǎn), 編譯后包的目標(biāo)文件不僅僅記錄包本身的導(dǎo)出信息, 目標(biāo)文件同時(shí)還記錄了包的依賴關(guān)系。 因此, 在編譯一個(gè)包的時(shí)候, 編譯器只需要讀取每個(gè)直接導(dǎo)入包的目標(biāo)文件, 而不需要遍歷所有依賴的的文件( 譯注: 很多都是重復(fù)的間接依賴) 。

  • 2. 導(dǎo)入路徑

  • 每個(gè)包是由一個(gè)全局唯一的字符串所標(biāo)識(shí)的導(dǎo)入路徑定位。 出現(xiàn)在import語句中的導(dǎo)入路徑也是字符串。
  • 就像我們?cè)?.6.1節(jié)提到過的, Go語言的規(guī)范并沒有指明包的導(dǎo)入路徑字符串的具體含義, 導(dǎo)入路徑的具體含義是由構(gòu)建工具來解釋的。 在本章, 我們將深入討論Go語言工具箱的功能,包括大家經(jīng)常使用的構(gòu)建測(cè)試等功能。 當(dāng)然, 也有第三方擴(kuò)展的工具箱存在。 例如, Google公司內(nèi)部的Go語言碼農(nóng), 他們就使用內(nèi)部的多語言構(gòu)建系統(tǒng)( 譯注: Google公司使用的是類似Bazel的構(gòu)建系統(tǒng), 支持多種編程語言, 目前該構(gòu)件系統(tǒng)還不能完整支持Windows環(huán)境) ,用不同的規(guī)則來處理包名字和定位包, 用不同的規(guī)則來處理單元測(cè)試等等, 因?yàn)檫@樣可以更緊密適配他們內(nèi)部環(huán)境。
  • 如果你計(jì)劃分享或發(fā)布包, 那么導(dǎo)入路徑最好是全球唯一的。 為了避免沖突, 所有非標(biāo)準(zhǔn)庫(kù)包的導(dǎo)入路徑建議以所在組織的互聯(lián)網(wǎng)域名為前綴; 而且這樣也有利于包的檢索。 例如, 上面的import語句導(dǎo)入了Go團(tuán)隊(duì)維護(hù)的HTML解析器和一個(gè)流行的第三方維護(hù)的MySQL驅(qū)動(dòng)。

  • 3. 包聲明

  • 在每個(gè)Go語音源文件的開頭都必須有包聲明語句。 包聲明語句的主要目的是確定當(dāng)前包被其它包導(dǎo)入時(shí)默認(rèn)的標(biāo)識(shí)符( 也稱為包名) 。

  • 例如, math/rand包的每個(gè)源文件的開頭都包含 package rand 包聲明語句, 所以當(dāng)你導(dǎo)入這個(gè)包, 你就可以用rand.Int、 rand.Float64類似的方式訪問包的成員。

  • 通常來說, 默認(rèn)的包名就是包導(dǎo)入路徑名的最后一段, 因此即使兩個(gè)包的導(dǎo)入路徑不同, 它們依然可能有一個(gè)相同的包名。 例如, math/rand包和crypto/rand包的包名都是rand。 稍后我們將看到如何同時(shí)導(dǎo)入兩個(gè)有相同包名的包。

  • 關(guān)于默認(rèn)包名一般采用導(dǎo)入路徑名的最后一段的約定也有三種例外情況。 第一個(gè)例外, 包對(duì)應(yīng)一個(gè)可執(zhí)行程序, 也就是main包, 這時(shí)候main包本身的導(dǎo)入路徑是無關(guān)緊要的。 名字為main的包是給go build( §10.7.3) 構(gòu)建命令一個(gè)信息, 這個(gè)包編譯完之后必須調(diào)用連接器生成一個(gè)可執(zhí)行程序。

  • 第二個(gè)例外, 包所在的目錄中可能有一些文件名是以test.go為后綴的Go源文件( 譯注: 前面必須有其它的字符, 因?yàn)橐?#96;`前綴的源文件是被忽略的) , 并且這些源文件聲明的包名也是以_test為后綴名的。 這種目錄可以包含兩種包: 一種普通包, 加一種則是測(cè)試的外部擴(kuò)展包。所有以_test為后綴包名的測(cè)試外部擴(kuò)展包都由go test命令獨(dú)立編譯, 普通包和測(cè)試的外部擴(kuò)展包是相互獨(dú)立的。 測(cè)試的外部擴(kuò)展包一般用來避免測(cè)試代碼中的循環(huán)導(dǎo)入依賴, 具體細(xì)節(jié)我們將在11.2.4節(jié)中介紹。

  • 第三個(gè)例外, 一些依賴版本號(hào)的管理工具會(huì)在導(dǎo)入路徑后追加版本號(hào)信息, 例如"gopkg.in/yaml.v2"。 這種情況下包的名字并不包含版本號(hào)后綴, 而是yaml。


  • 4. 導(dǎo)入聲明

  • 可以在一個(gè)Go語言源文件包聲明語句之后, 其它非導(dǎo)入聲明語句之前, 包含零到多個(gè)導(dǎo)入包聲明語句。 每個(gè)導(dǎo)入聲明可以單獨(dú)指定一個(gè)導(dǎo)入路徑, 也可以通過圓括號(hào)同時(shí)導(dǎo)入多個(gè)導(dǎo)入路徑。 下面兩個(gè)導(dǎo)入形式是等價(jià)的, 但是第二種形式更為常見
  • 導(dǎo)入的包之間可以通過添加空行來分組; 通常將來自不同組織的包獨(dú)自分組。 包的導(dǎo)入順序無關(guān)緊要, 但是在每個(gè)分組中一般會(huì)根據(jù)字符串順序排列。 ( gofmt和goimports工具都可以將不同分組導(dǎo)入的包獨(dú)立排序。 )
  • 如果我們想同時(shí)導(dǎo)入兩個(gè)有著名字相同的包, 例如math/rand包和crypto/rand包, 那么導(dǎo)入聲明必須至少為一個(gè)同名包指定一個(gè)新的包名以避免沖突。 這叫做導(dǎo)入包的重命名。
  • 導(dǎo)入包的重命名只影響當(dāng)前的源文件。 其它的源文件如果導(dǎo)入了相同的包, 可以用導(dǎo)入包原本默認(rèn)的名字或重命名為另一個(gè)完全不同的名字。
  • 導(dǎo)入包重命名是一個(gè)有用的特性, 它不僅僅只是為了解決名字沖突。 如果導(dǎo)入的一個(gè)包名很笨重, 特別是在一些自動(dòng)生成的代碼中, 這時(shí)候用一個(gè)簡(jiǎn)短名稱會(huì)更方便。 選擇用簡(jiǎn)短名稱重命名導(dǎo)入包時(shí)候最好統(tǒng)一, 以避免包名混亂。 選擇另一個(gè)包名稱還可以幫助避免和本地普通變量名產(chǎn)生沖突。 例如, 如果文件中已經(jīng)有了一個(gè)名為path的變量, 那么我們可以將"path"標(biāo)準(zhǔn)包重命名為pathpkg。
  • 每個(gè)導(dǎo)入聲明語句都明確指定了當(dāng)前包和被導(dǎo)入包之間的依賴關(guān)系。 如果遇到包循環(huán)導(dǎo)入的情況, Go語言的構(gòu)建工具將報(bào)告錯(cuò)誤。

  • 5. 包的匿名導(dǎo)入

  • 如果只是導(dǎo)入一個(gè)包而并不使用導(dǎo)入的包將會(huì)導(dǎo)致一個(gè)編譯錯(cuò)誤。 但是有時(shí)候我們只是想利用導(dǎo)入包而產(chǎn)生的副作用: 它會(huì)計(jì)算包級(jí)變量的初始化表達(dá)式和執(zhí)行導(dǎo)入包的init初始化函數(shù)( §2.6.2) 。 這時(shí)候我們需要抑制“unused import”編譯錯(cuò)誤, 我們可以用下劃線 _ 來重命名導(dǎo)入的包。 像往常一樣, 下劃線 _ 為空白標(biāo)識(shí)符, 并不能被訪問
  • 這個(gè)被稱為包的匿名導(dǎo)入。 它通常是用來實(shí)現(xiàn)一個(gè)編譯時(shí)機(jī)制, 然后通過在main主程序入口選擇性地導(dǎo)入附加的包。 首先, 讓我們看看如何使用該特性, 然后再看看它是如何工作的。
  • 標(biāo)準(zhǔn)庫(kù)的image圖像包包含了一個(gè) Decode 函數(shù), 用于從 io.Reader 接口讀取數(shù)據(jù)并解碼圖像, 它調(diào)用底層注冊(cè)的圖像解碼器來完成任務(wù), 然后返回image.Image類型的圖像。 使用 image.Decode 很容易編寫一個(gè)圖像格式的轉(zhuǎn)換工具, 讀取一種格式的圖像, 然后編碼為另一種圖像格式:
    gopl.io/ch10/jpeg
  • 如果我們將 gopl.io/ch3/mandelbrot ( §3.3) 的輸出導(dǎo)入到這個(gè)程序的標(biāo)準(zhǔn)輸入, 它將解碼輸入的PNG格式圖像, 然后轉(zhuǎn)換為JPEG格式的圖像輸出( 圖3.3) 。
  • 要注意image/png包的匿名導(dǎo)入語句。 如果沒有這一行語句, 程序依然可以編譯和運(yùn)行, 但是它將不能正確識(shí)別和解碼PNG格式的圖像:
  • 下面的代碼演示了它的工作機(jī)制。 標(biāo)準(zhǔn)庫(kù)還提供了GIF、 PNG和JPEG等格式圖像的解碼器,用戶也可以提供自己的解碼器, 但是為了保持程序體積較小, 很多解碼器并沒有被全部包含, 除非是明確需要支持的格式。 image.Decode函數(shù)在解碼時(shí)會(huì)依次查詢支持的格式列表。
  • 每個(gè)格式驅(qū)動(dòng)列表的每個(gè)入口指定了四件事情: 格式的名稱; 一個(gè)用于描述這種圖像數(shù)據(jù)開頭部分模式的字符串, 用于解碼器檢測(cè)識(shí)別; 一個(gè)Decode函數(shù)用于完成解碼圖像工作; 一個(gè)DecodeConfig函數(shù)用于解碼圖像的大小和顏色空間的信息。 每個(gè)驅(qū)動(dòng)入口是通過調(diào)用image.RegisterFormat函數(shù)注冊(cè), 一般是在每個(gè)格式包的init初始化函數(shù)中調(diào)用, 例如image/png包是這樣注冊(cè)的:
  • 最終的效果是, 主程序只需要匿名導(dǎo)入特定圖像驅(qū)動(dòng)包就可以用image.Decode解碼對(duì)應(yīng)格式的圖像了。
  • 數(shù)據(jù)庫(kù)包database/sql也是采用了類似的技術(shù), 讓用戶可以根據(jù)自己需要選擇導(dǎo)入必要的數(shù)據(jù)庫(kù)驅(qū)動(dòng)。 例如:

  • 6. 包和命名

  • 在本節(jié)中, 我們將提供一些關(guān)于Go語言獨(dú)特的包和成員命名的約定。
  • 當(dāng)創(chuàng)建一個(gè)包, 一般要用短小的包名, 但也不能太短導(dǎo)致難以理解。 標(biāo)準(zhǔn)庫(kù)中最常用的包有bufio、 bytes、 flag、 fmt、 http、 io、 json、 os、 sort、 sync和time等包。
  • 它們的名字都簡(jiǎn)潔明了。 例如, 不要將一個(gè)類似imageutil或ioutilis的通用包命名為util, 雖然它看起來很短小。 要盡量避免包名使用可能被經(jīng)常用于局部變量的名字, 這樣可能導(dǎo)致用戶重命名導(dǎo)入包, 例如前面看到的path包。
  • 包名一般采用單數(shù)的形式。 標(biāo)準(zhǔn)庫(kù)的bytes、 errors和strings使用了復(fù)數(shù)形式, 這是為了避免和預(yù)定義的類型沖突, 同樣還有g(shù)o/types是為了避免和type關(guān)鍵字沖突。
  • 要避免包名有其它的含義。 例如, 2.5節(jié)中我們的溫度轉(zhuǎn)換包最初使用了temp包名, 雖然并沒有持續(xù)多久。 但這是一個(gè)糟糕的嘗試, 因?yàn)閠emp幾乎是臨時(shí)變量的同義詞。 然后我們有一段時(shí)間使用了temperature作為包名, 雖然名字并沒有表達(dá)包的真實(shí)用途。 最后我們改成了和strconv標(biāo)準(zhǔn)包類似的tempconv包名, 這個(gè)名字比之前的就好多了。
  • 現(xiàn)在讓我們看看如何命名包的成員。 由于是通過包的導(dǎo)入名字引入包里面的成員, 例如fmt.Println, 同時(shí)包含了包名和成員名信息。 因此, 我們一般并不需要關(guān)注Println的具體內(nèi)容, 因?yàn)閒mt包名已經(jīng)包含了這個(gè)信息。 當(dāng)設(shè)計(jì)一個(gè)包的時(shí)候, 需要考慮包名和成員名兩個(gè)部分如何很好地配合。 下面有一些例子:
  • 我們可以看到一些常用的命名模式。 strings包提供了和字符串相關(guān)的諸多操作:
  • 字符串string本身并沒有出現(xiàn)在每個(gè)成員名字中。 因?yàn)橛脩魰?huì)這樣引用這些成員strings.Index、 strings.Replacer等。
  • 其它一些包, 可能只描述了單一的數(shù)據(jù)類型, 例如html/template和math/rand等, 只暴露一個(gè)主要的數(shù)據(jù)結(jié)構(gòu)和與它相關(guān)的方法, 還有一個(gè)以New命名的函數(shù)用于創(chuàng)建實(shí)例。
  • 這可能導(dǎo)致一些名字重復(fù), 例如template.Template或rand.Rand, 這就是為什么這些種類的包名往往特別短的原因之一。
  • 在另一個(gè)極端, 還有像net/http包那樣含有非常多的名字和種類不多的數(shù)據(jù)類型, 因?yàn)樗鼈兌际且獔?zhí)行一個(gè)復(fù)雜的復(fù)合任務(wù)。 盡管有將近二十種類型和更多的函數(shù), 但是包中最重要的成員名字卻是簡(jiǎn)單明了的: Get、 Post、 Handle、 Error、 Client、 Server等。

  • 7. 工具

  • 本章剩下的部分將討論Go語言工具箱的具體功能, 包括如何下載、 格式化、 構(gòu)建、 測(cè)試和安裝Go語言編寫的程序。
  • Go語言的工具箱集合了一系列的功能的命令集。 它可以看作是一個(gè)包管理器( 類似于Linux中的apt和rpm工具) , 用于包的查詢、 計(jì)算的包依賴關(guān)系、 從遠(yuǎn)程版本控制系統(tǒng)和下載它們等任務(wù)。 它也是一個(gè)構(gòu)建系統(tǒng), 計(jì)算文件的依賴關(guān)系, 然后調(diào)用編譯器、 匯編器和連接器構(gòu)建程序, 雖然它故意被設(shè)計(jì)成沒有標(biāo)準(zhǔn)的make命令那么復(fù)雜。 它也是一個(gè)單元測(cè)試和基準(zhǔn)測(cè)試的驅(qū)動(dòng)程序, 我們將在第11章討論測(cè)試話題。
  • Go語言工具箱的命令有著類似“瑞士軍刀”的風(fēng)格, 帶著一打子的子命令, 有一些我們經(jīng)常用到, 例如get、 run、 build和fmt等。 你可以運(yùn)行g(shù)o或go help命令查看內(nèi)置的幫助文檔, 為了查詢方便, 我們列出了最常用的命令:
  • 為了達(dá)到零配置的設(shè)計(jì)目標(biāo), Go語言的工具箱很多地方都依賴各種約定。 例如, 根據(jù)給定的源文件的名稱, Go語言的工具可以找到源文件對(duì)應(yīng)的包, 因?yàn)槊總€(gè)目錄只包含了單一的包,并且到的導(dǎo)入路徑和工作區(qū)的目錄結(jié)構(gòu)是對(duì)應(yīng)的。 給定一個(gè)包的導(dǎo)入路徑, Go語言的工具可以找到對(duì)應(yīng)的目錄中沒個(gè)實(shí)體對(duì)應(yīng)的源文件。 它還可以根據(jù)導(dǎo)入路徑找到存儲(chǔ)代碼倉(cāng)庫(kù)的遠(yuǎn)程服務(wù)器的URL。
  • 1. 工作區(qū)結(jié)構(gòu)

  • 對(duì)于大多數(shù)的Go語言用戶, 只需要配置一個(gè)名叫GOPATH的環(huán)境變量, 用來指定當(dāng)前工作目錄即可。 當(dāng)需要切換到不同工作區(qū)的時(shí)候, 只要更新GOPATH就可以了。 例如, 我們?cè)诰帉懕緯鴷r(shí)將GOPATH設(shè)置為 $HOME/gobook :
  • 當(dāng)你用前面介紹的命令下載本書全部的例子源碼之后, 你的當(dāng)前工作區(qū)的目錄結(jié)構(gòu)應(yīng)該是這樣的:
  • GOPATH對(duì)應(yīng)的工作區(qū)目錄有三個(gè)子目錄。 其中src子目錄用于存儲(chǔ)源代碼。 每個(gè)包被保存在與$GOPATH/src的相對(duì)路徑為包導(dǎo)入路徑的子目錄中, 例如gopl.io/ch1/helloworld相對(duì)應(yīng)的路徑目錄。 我們看到, 一個(gè)GOPATH工作區(qū)的src目錄中可能有多個(gè)獨(dú)立的版本控制系統(tǒng), 例如gopl.io和golang.org分別對(duì)應(yīng)不同的Git倉(cāng)庫(kù)。 其中pkg子目錄用于保存編譯后的包的目標(biāo)文件, bin子目錄用于保存編譯后的可執(zhí)行程序, 例如helloworld可執(zhí)行程序。
  • 第二個(gè)環(huán)境變量GOROOT用來指定Go的安裝目錄, 還有它自帶的標(biāo)準(zhǔn)庫(kù)包的位置。
  • GOROOT的目錄結(jié)構(gòu)和GOPATH類似, 因此存放fmt包的源代碼對(duì)應(yīng)目錄應(yīng)該為$GOROOT/src/fmt。 用戶一般不需要設(shè)置GOROOT, 默認(rèn)情況下Go語言安裝工具會(huì)將其設(shè)置為安裝的目錄路徑
  • 其中 go env 命令用于查看Go語音工具涉及的所有環(huán)境變量的值, 包括未設(shè)置環(huán)境變量的默認(rèn)值。 GOOS環(huán)境變量用于指定目標(biāo)操作系統(tǒng)( 例如android、 linux、 darwin或windows) ,GOARCH環(huán)境變量用于指定處理器的類型, 例如amd64、 386或arm等。 雖然GOPATH環(huán)境變量是唯一必需要設(shè)置的, 但是其它環(huán)境變量也會(huì)偶爾用到。
  • 2. 下載包

  • 使用Go語言工具箱的go命令, 不僅可以根據(jù)包導(dǎo)入路徑找到本地工作區(qū)的包, 甚至可以從互聯(lián)網(wǎng)上找到和更新包。
  • 使用命令 go get 可以下載一個(gè)單一的包或者用 … 下載整個(gè)子目錄里面的每個(gè)包。 Go語言工具箱的go命令同時(shí)計(jì)算并下載所依賴的每個(gè)包, 這也是前一個(gè)例子中g(shù)olang.org/x/net/html自動(dòng)出現(xiàn)在本地工作區(qū)目錄的原因。
  • 一旦 go get 命令下載了包, 然后就是安裝包或包對(duì)應(yīng)的可執(zhí)行的程序。 我們將在下一節(jié)再關(guān)注它的細(xì)節(jié), 現(xiàn)在只是展示整個(gè)下載過程是如何的簡(jiǎn)單。 第一個(gè)命令是獲取golint工具, 它用于檢測(cè)Go源代碼的編程風(fēng)格是否有問題。 第二個(gè)命令是用golint命令對(duì)2.6.2節(jié)的gopl.io/ch2/popcount包代碼進(jìn)行編碼風(fēng)格檢查。 它友好地報(bào)告了忘記了包的文檔:
  • go get 命令支持當(dāng)前流行的托管網(wǎng)站GitHub、 Bitbucket和Launchpad, 可以直接向它們的版本控制系統(tǒng)請(qǐng)求代碼。 對(duì)于其它的網(wǎng)站, 你可能需要指定版本控制系統(tǒng)的具體路徑和協(xié)議,例如 Git或Mercurial。 運(yùn)行 go help importpath 獲取相關(guān)的信息。
  • go get 命令獲取的代碼是真實(shí)的本地存儲(chǔ)倉(cāng)庫(kù), 而不僅僅只是復(fù)制源文件, 因此你依然可以使用版本管理工具比較本地代碼的變更或者切換到其它的版本。 例如golang.org/x/net包目錄對(duì)應(yīng)一個(gè)Git倉(cāng)庫(kù):
  • 需要注意的是導(dǎo)入路徑含有的網(wǎng)站域名和本地Git倉(cāng)庫(kù)對(duì)應(yīng)遠(yuǎn)程服務(wù)地址并不相同, 真實(shí)的Git地址是go.googlesource.com。 這其實(shí)是Go語言工具的一個(gè)特性, 可以讓包用一個(gè)自定義的導(dǎo)入路徑, 但是真實(shí)的代碼卻是由更通用的服務(wù)提供, 例如googlesource.com或github.com。因?yàn)轫撁?https://golang.org/x/net/html 包含了如下的元數(shù)據(jù), 它告訴Go語言的工具當(dāng)前包真實(shí)的Git倉(cāng)庫(kù)托管地址:
  • 如果指定 -u 命令行標(biāo)志參數(shù), go get 命令將確保所有的包和依賴的包的版本都是最新的,然后重新編譯和安裝它們。 如果不包含該標(biāo)志參數(shù)的話, 而且如果包已經(jīng)在本地存在, 那么代碼那么將不會(huì)被自動(dòng)更新。
  • go get -u 命令只是簡(jiǎn)單地保證每個(gè)包是最新版本, 如果是第一次下載包則是比較很方便的; 但是對(duì)于發(fā)布程序則可能是不合適的, 因?yàn)楸镜爻绦蚩赡苄枰獙?duì)依賴的包做精確的版本依賴管理。 通常的解決方案是使用vendor的目錄用于存儲(chǔ)依賴包的固定版本的源代碼, 對(duì)本地依賴的包的版本更新也是謹(jǐn)慎和持續(xù)可控的。 在Go1.5之前, 一般需要修改包的導(dǎo)入路徑,所以復(fù)制后golang.org/x/net/html導(dǎo)入路徑可能會(huì)變?yōu)間opl.io/vendor/golang.org/x/net/html。最新的Go語言命令已經(jīng)支持vendor特性, 但限于篇幅這里并不討論vendor的具體細(xì)節(jié)。 不過可以通過 go help gopath 命令查看Vendor的幫助文檔。

  • 3. 構(gòu)建包

  • go build 命令編譯命令行參數(shù)指定的每個(gè)包。 如果包是一個(gè)庫(kù), 則忽略輸出結(jié)果; 這可以用于檢測(cè)包的可以正確編譯的。 如果包的名字是main, go build 將調(diào)用連接器在當(dāng)前目錄創(chuàng)建一個(gè)可執(zhí)行程序; 以導(dǎo)入路徑的最后一段作為可執(zhí)行程序的名字。
  • 因?yàn)槊總€(gè)目錄只包含一個(gè)包, 因此每個(gè)對(duì)應(yīng)可執(zhí)行程序或者叫Unix術(shù)語中的命令的包, 會(huì)要求放到一個(gè)獨(dú)立的目錄中。 這些目錄有時(shí)候會(huì)放在名叫cmd目錄的子目錄下面, 例如用于提供Go文檔服務(wù)的golang.org/x/tools/cmd/godoc命令就是放在cmd子目錄( §10.7.4) 。
  • 每個(gè)包可以由它們的導(dǎo)入路徑指定, 就像前面看到的那樣, 或者用一個(gè)相對(duì)目錄的路徑知指定, 相對(duì)路徑必須以 . 或 … 開頭。 如果沒有指定參數(shù), 那么默認(rèn)指定為當(dāng)前目錄對(duì)應(yīng)的包。 下面的命令用于構(gòu)建同一個(gè)包, 雖然它們的寫法各不相同:
  • 或者:
  • 或者:
  • 但不能這樣:
  • 也可以指定包的源文件列表, 這一般這只用于構(gòu)建一些小程序或做一些臨時(shí)性的實(shí)驗(yàn)。 如果是main包, 將會(huì)以第一個(gè)Go源文件的基礎(chǔ)文件名作為最終的可執(zhí)行程序的名字。
  • 特別是對(duì)于這類一次性運(yùn)行的程序, 我們希望盡快的構(gòu)建并運(yùn)行它。 go run 命令實(shí)際上是結(jié)合了構(gòu)建和運(yùn)行的兩個(gè)步驟:
  • 第一行的參數(shù)列表中, 第一個(gè)不是以 .go 結(jié)尾的將作為可執(zhí)行程序的參數(shù)運(yùn)行。
  • 默認(rèn)情況下, go build 命令構(gòu)建指定的包和它依賴的包, 然后丟棄除了最后的可執(zhí)行文件之外所有的中間編譯結(jié)果。 依賴分析和編譯過程雖然都是很快的, 但是隨著項(xiàng)目增加到幾十個(gè)包和成千上萬行代碼, 依賴關(guān)系分析和編譯時(shí)間的消耗將變的可觀, 有時(shí)候可能需要幾秒種, 即使這些依賴項(xiàng)沒有改變。
  • go install 命令和 go build 命令很相似, 但是它會(huì)保存每個(gè)包的編譯成果, 而不是將它們都丟棄。 被編譯的包會(huì)被保存到GOPATH/pkg目錄下,目錄路徑和src目錄路徑對(duì)應(yīng),可執(zhí)行程序被保存到GOPATH/pkg目錄下, 目錄路徑和 src目錄路徑對(duì)應(yīng), 可執(zhí)行程序被保存到GOPATH/pkgsrc對(duì)應(yīng)執(zhí)GOPATH/bin目錄。 ( 很多用戶會(huì)將$GOPATH/bin添加到可執(zhí)行程序的搜索列表中。 ) 還有, go install 命令和 go build 命令都不會(huì)重新編譯沒有發(fā)生變化的包, 這可以使后續(xù)構(gòu)建更快捷。 為了方便編譯依賴的包, go build -i 命令將安裝每個(gè)目標(biāo)所依賴的包。
  • 因?yàn)榫幾g對(duì)應(yīng)不同的操作系統(tǒng)平臺(tái)和CPU架構(gòu), go install 命令會(huì)將編譯結(jié)果安裝到GOOS和GOARCH對(duì)應(yīng)的目錄。 例如, 在Mac系統(tǒng), golang.org/x/net/html包將被安裝到$GOPATH/pkg/darwin_amd64目錄下的golang.org/x/net/html.a文件。
  • 針對(duì)不同操作系統(tǒng)或CPU的交叉構(gòu)建也是很簡(jiǎn)單的。 只需要設(shè)置好目標(biāo)對(duì)應(yīng)的GOOS和GOARCH, 然后運(yùn)行構(gòu)建命令即可。 下面交叉編譯的程序?qū)⑤敵鏊诰幾g時(shí)操作系統(tǒng)和CPU類型:
  • gopl.io/ch10/cross
  • 下面以64位和32位環(huán)境分別執(zhí)行程序:
  • 有些包可能需要針對(duì)不同平臺(tái)和處理器類型使用不同版本的代碼文件, 以便于處理底層的可移植性問題或提供為一些特定代碼提供優(yōu)化。 如果一個(gè)文件名包含了一個(gè)操作系統(tǒng)或處理器類型名字, 例如net_linux.go或asm_amd64.s, Go語言的構(gòu)建工具將只在對(duì)應(yīng)的平臺(tái)編譯這些文件。 還有一個(gè)特別的構(gòu)建注釋注釋可以提供更多的構(gòu)建過程控制。 例如, 文件中可能包含下面的注釋:
  • 在包聲明和包注釋的前面, 該構(gòu)建注釋參數(shù)告訴 go build 只在編譯程序?qū)?yīng)的目標(biāo)操作系統(tǒng)是Linux或Mac OS X時(shí)才編譯這個(gè)文件。 下面的構(gòu)建注釋則表示不編譯這個(gè)文件:
  • 更多細(xì)節(jié), 可以參考go/build包的構(gòu)建約束部分的文檔。
  • 4. 包文檔

  • Go語言的編碼風(fēng)格鼓勵(lì)為每個(gè)包提供良好的文檔。 包中每個(gè)導(dǎo)出的成員和包聲明前都應(yīng)該包含目的和用法說明的注釋。
  • Go語言中包文檔注釋一般是完整的句子, 第一行是包的摘要說明, 注釋后僅跟著包聲明語句。 注釋中函數(shù)的參數(shù)或其它的標(biāo)識(shí)符并不需要額外的引號(hào)或其它標(biāo)記注明。 例如, 下面是fmt.Fprintf的文檔注釋。
  • Fprintf函數(shù)格式化的細(xì)節(jié)在fmt包文檔中描述。 如果注釋后僅跟著包聲明語句, 那注釋對(duì)應(yīng)整個(gè)包的文檔。 包文檔對(duì)應(yīng)的注釋只能有一個(gè)( 譯注: 其實(shí)可以有多個(gè), 它們會(huì)組合成一個(gè)包文檔注釋) , 包注釋可以出現(xiàn)在任何一個(gè)源文件中。 如果包的注釋內(nèi)容比較長(zhǎng), 一般會(huì)放到一個(gè)獨(dú)立的源文件中; fmt包注釋就有300行之多。 這個(gè)專門用于保存包文檔的源文件通常叫doc.go。
  • 好的文檔并不需要面面俱到, 文檔本身應(yīng)該是簡(jiǎn)潔但可不忽略的。 事實(shí)上, Go語言的風(fēng)格更喜歡簡(jiǎn)潔的文檔, 并且文檔也是需要像代碼一樣維護(hù)的。 對(duì)于一組聲明語句, 可以用一個(gè)精煉的句子描述, 如果是顯而易見的功能則并不需要注釋。
  • 在本書中, 只要空間允許, 我們之前很多包聲明都包含了注釋文檔, 但你可以從標(biāo)準(zhǔn)庫(kù)中發(fā)現(xiàn)很多更好的例子。 有兩個(gè)工具可以幫到你。
  • 首先是 go doc 命令, 該命令打印包的聲明和每個(gè)成員的文檔注釋, 下面是整個(gè)包的文檔:
  • 或者是某個(gè)具體包成員的注釋文檔:
  • 或者是某個(gè)具體包的一個(gè)方法的注釋文檔:
  • 該命令并不需要輸入完整的包導(dǎo)入路徑或正確的大小寫。 下面的命令將打印encoding/json包的 (*json.Decoder).Decode 方法的文檔:
  • 第二個(gè)工具, 名字也叫g(shù)odoc, 它提供可以相互交叉引用的HTML頁面, 但是包含和 godoc 命令相同以及更多的信息。 10.1節(jié)演示了time包的文檔, 11.6節(jié)將看到godoc演示可以交互的示例程序。 godoc的在線服務(wù) https://godoc.org , 包含了成千上萬的開源包的檢索工具。
  • 你也可以在自己的工作區(qū)目錄運(yùn)行g(shù)odoc服務(wù)。 運(yùn)行下面的命令, 然后在瀏覽器查看http://localhost:8000/pkg 頁面:
  • 5. 內(nèi)部包

  • 在Go語音程序中, 包的封裝機(jī)制是一個(gè)重要的特性。 沒有導(dǎo)出的標(biāo)識(shí)符只在同一個(gè)包內(nèi)部可以訪問, 而導(dǎo)出的標(biāo)識(shí)符則是面向全宇宙都是可見的。
  • 有時(shí)候, 一個(gè)中間的狀態(tài)可能也是有用的, 對(duì)于一小部分信任的包是可見的, 但并不是對(duì)所有調(diào)用者都可見。 例如, 當(dāng)我們計(jì)劃將一個(gè)大的包拆分為很多小的更容易維護(hù)的子包, 但是我們并不想將內(nèi)部的子包結(jié)構(gòu)也完全暴露出去。 同時(shí), 我們可能還希望在內(nèi)部子包之間共享一些通用的處理包, 或者我們只是想實(shí)驗(yàn)一個(gè)新包的還并不穩(wěn)定的接口, 暫時(shí)只暴露給一些受限制的用戶使用。
  • 為了滿足這些需求, Go語言的構(gòu)建工具對(duì)包含internal名字的路徑段的包導(dǎo)入路徑做了特殊處理。 這種包叫internal包, 一個(gè)internal包只能被和internal目錄有同一個(gè)父目錄的包所導(dǎo)入。 例如, net/http/internal/chunked內(nèi)部包只能被net/http/httputil或net/http包導(dǎo)入, 但是不能被net/url包導(dǎo)入。 不過net/url包卻可以導(dǎo)入net/http/httputil包。
  • 6. 查詢包

  • go list 命令可以查詢可用包的信息。 其最簡(jiǎn)單的形式, 可以測(cè)試包是否在工作區(qū)并打印它的導(dǎo)入路徑:
  • go list 命令的參數(shù)還可以用 “…” 表示匹配任意的包的導(dǎo)入路徑。 我們可以用它來列表工作區(qū)中的所有包:
  • 或者是特定子目錄下的所有包:
  • 或者是和某個(gè)主題相關(guān)的所有包:
  • go list 命令還可以獲取每個(gè)包完整的元信息, 而不僅僅只是導(dǎo)入路徑, 這些元信息可以以不同格式提供給用戶。 其中 -json 命令行參數(shù)表示用JSON格式打印每個(gè)包的元信息。
  • 命令行參數(shù) -f 則允許用戶使用text/template包( §4.6) 的模板語言定義輸出文本的格式。 下面的命令將打印strconv包的依賴的包, 然后用join模板函數(shù)將結(jié)果鏈接為一行, 連接時(shí)每個(gè)結(jié)果之間用一個(gè)空格分隔:
  • 命令行參數(shù) -f 則允許用戶使用text/template包( §4.6) 的模板語言定義輸出文本的格式。 下面的命令將打印strconv包的依賴的包, 然后用join模板函數(shù)將結(jié)果鏈接為一行, 連接時(shí)每個(gè)結(jié)果之間用一個(gè)空格分隔:
  • 譯注: 上面的命令在Windows的命令行運(yùn)行會(huì)遇到 template: main:1: unclosed action 的錯(cuò)誤。 產(chǎn)生這個(gè)錯(cuò)誤的原因是因?yàn)槊钚袑?duì)命令中的 " " 參數(shù)進(jìn)行了轉(zhuǎn)義處理。 可以按照下面的方法解決轉(zhuǎn)義字符串的問題:
  • 下面的命令打印compress子目錄下所有包的依賴包列表:
  • 譯注: Windows下有同樣有問題, 要避免轉(zhuǎn)義字符串的干擾:
  • go list 命令對(duì)于一次性的交互式查詢或自動(dòng)化構(gòu)建或測(cè)試腳本都很有幫助。 我們將在11.2.4節(jié)中再次使用它。 每個(gè)子命令的更多信息, 包括可設(shè)置的字段和意義, 可以用 go helplist 命令查看。
  • 在本章, 我們解釋了Go語言工具中除了測(cè)試命令之外的所有重要的子命令。 在下一章, 我們將看到如何用 go test 命令去運(yùn)行Go語言程序中的測(cè)試代碼。
  • 總結(jié)

    以上是生活随笔為你收集整理的《Go语言圣经》学习笔记 第十章 包和工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。