真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Go源碼閱讀之什么是flag包

這篇文章主要講解了“Go源碼閱讀之什么是flag 包”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Go源碼閱讀之什么是flag 包”吧!

成都創(chuàng)新互聯(lián)公司專注于武鳴企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城網(wǎng)站制作。武鳴網(wǎng)站建設(shè)公司,為武鳴等地區(qū)提供建站服務(wù)。全流程定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

簡(jiǎn)介

flag 包是 Go 里用于解析命令行參數(shù)的包。為什么選擇它作為第一個(gè)閱讀的包,因?yàn)樗拇a量少。其核心代碼只有一個(gè) 1000 不到的 flag.go 文件。

文件結(jié)構(gòu)

flag 包的文件結(jié)構(gòu)很簡(jiǎn)單,就一層。一個(gè)文件夾里放了 5 個(gè)文件,其文件及其作用如下:

  • flag.go

    flag 的核心包,實(shí)現(xiàn)了命令行參數(shù)解析的所有功能

  • export_test.go

    測(cè)試的實(shí)用工具,定義了所有測(cè)試需要的基礎(chǔ)變量和函數(shù)

  • flag_test.go

    flag 的測(cè)試文件,包含了 17 個(gè)測(cè)試單元

  • example_test.go

    flag 的樣例文件,介紹了 flag 包的三種常用的用法樣例

  • example_value_test.go

    flag 的樣例文件,介紹了一個(gè)更復(fù)雜的樣例

運(yùn)行測(cè)試

我先介紹一下 Go 的運(yùn)行環(huán)境。

# 通過 brew install go 安裝,源碼位置為 $GOROOT/src
GOROOT=/usr/local/opt/go/libexec
# 閱讀的源碼通過 go get -v -d github.com/haojunyu/go 下載,源碼位置為 $GOPATH/src/github.com
GOPATH=$HOME/go

單獨(dú)測(cè)試 flag 包踩過的坑:

  1. 無法針對(duì)單個(gè)文件進(jìn)行測(cè)試,需要針對(duì)包。

這里重點(diǎn)說一下 export_test.go 文件,它是flag包的一部分package flag,但是它確實(shí)專門為測(cè)試而存在的,說白了也就一個(gè)ResetForTesting方法,用來清除所有命令參數(shù)狀態(tài)并且直接設(shè)置Usage函數(shù)。該方法會(huì)在測(cè)試用例中被頻繁使用。所以單獨(dú)運(yùn)行以下命令會(huì)報(bào)錯(cuò)"flag_test.go:30:2: undefined: ResetForTesting"

# 測(cè)試當(dāng)前目錄(報(bào)錯(cuò))
go test -v .
# 測(cè)試包
go test -v flag
  1. go test -v flag 測(cè)試的源碼是 $GOROOT/src 下的(以我當(dāng)前的測(cè)試環(huán)境)

指定 flag 包后,實(shí)際運(yùn)行的源碼是 $GOROOT 下的,這個(gè)應(yīng)該和我的安裝方式有關(guān)系。

總結(jié)

接口轉(zhuǎn)換能實(shí)現(xiàn)類似 C++ 中模板的功能

flag 包中定義了一個(gè)結(jié)構(gòu)體類型叫 Flag,它用來存放一個(gè)命令參數(shù),其定義如下。

// A Flag represents the state of a flag.
// 結(jié)構(gòu)體Flag表示一個(gè)參數(shù)的所有信息,包括名稱,幫助信息,實(shí)際值和默認(rèn)值
type Flag struct {
	Name     string // name as it appears on command line名稱
	Usage    string // help message幫助信息
	Value    Value  // value as set實(shí)現(xiàn)了取值/賦值方法的接口
	DefValue string // default value (as text); for usage message默認(rèn)值
}

其中命令參數(shù)的值是一個(gè) Value 接口類型,其定義如下:

// Set is called once, in command line order, for each flag present.
// The flag package may call the String method with a zero-valued receiver,
// such as a nil pointer.
// 接口Value是個(gè)接口,在結(jié)構(gòu)體Flag中用來存儲(chǔ)每個(gè)參數(shù)的動(dòng)態(tài)值(參數(shù)類型格式各樣)
type Value interface {
	String() string   // 取值方法
	Set(string) error // 賦值方法
}

為什么這么做?因?yàn)檫@樣做能夠?qū)崿F(xiàn)類似模板的功能。任何一個(gè)類型 T 只要實(shí)現(xiàn)了 Value 接口里的 StringSet 方法,那么該類型 T 的變量 v 就可以轉(zhuǎn)換成 Value 接口類型,并使用 String 來取值,使用 Set 來賦值。這樣就能完美的解決不同類型使用相同的代碼操作目的,和 C++ 中的模板有相同的功效。

函數(shù) vs 方法

函數(shù)和方法都是一組一起執(zhí)行一個(gè)任務(wù)的語句,二者的區(qū)別在于調(diào)用者不同,函數(shù)的調(diào)用者是包 package,而方法的調(diào)用者是接受者 receiver。在 flag 的源碼中,有太多的函數(shù)里面只有一行,就是用包里的變量 CommandLine 調(diào)用同名方法。

// Parsed reports whether f.Parse has been called.
// Parsed方法: 命令行參數(shù)是否已經(jīng)解析
func (f *FlagSet) Parsed() bool {
	return f.parsed
}

// Parsed reports whether the command-line flags have been parsed.
func Parsed() bool {
	return CommandLine.Parsed()
}

new vs make

newmake 是 Go 語言中兩種內(nèi)存分配原語。二者所做的事情和針對(duì)的類型都不一樣。 new 和其他編程語言中的關(guān)鍵字功能類似,都是向系統(tǒng)申請(qǐng)一段內(nèi)存空間來存儲(chǔ)對(duì)應(yīng)類型的數(shù)據(jù),但又有些區(qū)別,區(qū)別在于它會(huì)將該片空間置零。也就是說 new(T) 會(huì)根據(jù)類型 T 在堆上 申請(qǐng)一片置零的內(nèi)存空間,并返回指針 *Tmake 只針對(duì)切片,映射和信道三種數(shù)據(jù)類型 T 的構(gòu)建,并返回類型為 T 的一個(gè)已經(jīng)初始化(而非零)的值。原因是這三種數(shù)據(jù)類型都是引用數(shù)據(jù)類型,在使用前必須初始化。就像切片是一個(gè)具有三項(xiàng)內(nèi)容的描述符,包含一個(gè)指向數(shù)組的指針,長度和容量。通過 make 創(chuàng)建對(duì)應(yīng)類型的變量過程是先分配一段空間,接著根據(jù)對(duì)應(yīng)的描述符來創(chuàng)建對(duì)應(yīng)的類型變量。關(guān)于 make 的細(xì)節(jié)可以看 draveness 寫的 Go語言設(shè)計(jì)與實(shí)現(xiàn)。

// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
	p := new(bool)
	f.BoolVar(p, name, value, usage)
	return p
}


// sortFlags returns the flags as a slice in lexicographical sorted order.
// sortFlags函數(shù):按字典順序排序命令參數(shù),并返回Flag的切片
func sortFlags(flags map[string]*Flag) []*Flag {
	result := make([]*Flag, len(flags))
	i := 0
	for _, f := range flags {
		result[i] = f
		i++
	}
	sort.Slice(result, func(i, j int) bool {
		return result[i].Name < result[j].Name
	})
	return result
}

指針賦值給接口變量

Go 中的接口有兩層含義,第一層是一組方法(不是函數(shù))的簽名,它需要接受者(具體類型 T 或具體類型指針 *T )來實(shí)現(xiàn)細(xì)節(jié);另一層是一個(gè)類型,而該類型能接受所有現(xiàn)實(shí)該接受的接受者。深入理解接口的概念可以細(xì)讀 Go語言設(shè)計(jì)與實(shí)現(xiàn)之接口。在 flag 包中的 StringVar 方法中newStringValue(value, p)返回的是 *stringValue 類型,而該類型(接受者)實(shí)現(xiàn)了 Value 接口( StringSet 方法),此時(shí)該類型就可以賦值給 Value 接口變量。

// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
// StringVar方法:將命令行參數(shù)的默認(rèn)值value賦值給變量*p,并生成結(jié)構(gòu)Flag并置于接受者中f.formal
func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
	f.Var(newStringValue(value, p), name, usage) // newStringValue返回值是*stringValue類型,之所以能賦值給Value接口是因?yàn)閚ewStringValue實(shí)現(xiàn)Value接口時(shí)定義的接受者為*stringValue
}

flag文件夾中有flag_test

flag 文件夾下有 flag_test 包,是因?yàn)樵撐募A下包含了核心代碼 flag.go 和測(cè)試代碼 *_test.go 。這兩部分代碼并沒有通過文件夾來區(qū)分。所以該 flag_test 包存在的意義是將測(cè)試代碼與核心代碼區(qū)分出來。而該包被引用時(shí)只會(huì)使用到核心代碼。

// example_test.go
package flag_test

作用域

關(guān)于作用域 Golang變量作用域 和 GO語言圣經(jīng)中關(guān)于作用域 都有了詳細(xì)的介紹,前者更通俗易懂些,后者更專業(yè)些。在 flag 包的 TestUsage 測(cè)試樣例中,因?yàn)?func(){called=true} 是在函數(shù) TestUsage 中定義函數(shù),并且直接作為形參傳遞給 ResetForTesting 函數(shù),所以該函數(shù)是和局部變量 called 是同級(jí)的,當(dāng)然在該函數(shù)中給該變量賦值也是合理的。

//  called變量的作用域
func TestUsage(t *testing.T) {
	called := false
	// 變量called的作用域
	ResetForTesting(func() { called = true })
	if CommandLine.Parse([]string{"-x"}) == nil {
		t.Error("parse did not fail for unknown flag")
	} else {
		t.Error("hahahh")
	}
	if !called {
		t.Error("did not call Usage for unknown flag")
	}
}

感謝各位的閱讀,以上就是“Go源碼閱讀之什么是flag 包”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Go源碼閱讀之什么是flag 包這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!


網(wǎng)頁名稱:Go源碼閱讀之什么是flag包
鏈接URL:http://weahome.cn/article/ggojsh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部