如果你需要一個通用的解決方案,你可以使用包reflect
做到這一點,但最好避免它如果可能的話(例如,如果您知道編譯時的類型和「路徑」,只需使用字段selectors和index expressions)。
這是一個演示。該設置由string
元素指定一個「深」值一個輔助函數可能看起來像這樣:
func set(d interface{}, value interface{}, path ...string) {
v := reflect.ValueOf(d)
for _, s := range path {
v = index(v, s)
}
v.Set(reflect.ValueOf(value))
}
上面使用可能看起來像這樣的index()
功能:
func index(v reflect.Value, idx string) reflect.Value {
if i, err := strconv.Atoi(idx); err == nil {
return v.Index(i)
}
return v.FieldByName(idx)
}
這是我們如何能夠測試:
type Foo struct {
Children []Foo
A int
}
func main() {
x := []Foo{
{
Children: []Foo{
{
Children: []Foo{
{
A: 1,
},
},
},
},
},
}
fmt.Printf("%+v\n", x)
path := "0.Children.0.Children.0.A"
set(x, 2, strings.Split(path, ".")...)
fmt.Printf("%+v\n", x)
}
輸出(嘗試在Go Playground):
[{Children:[{Children:[{Children:[] A:1}] A:0}] A:0}]
[{Children:[{Children:[{Children:[] A:2}] A:0}] A:0}]
如可以從輸出,由string
路徑"0.Children.0.Children.0.A"
從初始1
變爲2
表示的「深」字段A
看到。
注意結構(在這種情況下Foo.A
和Foo.Children
)的字段必須出口(必須以大寫字母),否則其他的包將無法訪問這些領域,其價值無法用包reflect
改變。
沒有反射,已知類型和 「路徑」 之前,就可以這樣來進行(繼續前面的例子):
f := &x[0].Children[0].Children[0]
fmt.Printf("%+v\n", f)
f.A = 3
fmt.Printf("%+v\n", f)
輸出(嘗試在Go Playground):
&{Children:[] A:2}
&{Children:[] A:3}
這樣做的一般溶液(無反射):
func getFoo(x []Foo, path ...string) (f *Foo) {
for _, s := range path {
if i, err := strconv.Atoi(s); err != nil {
panic(err)
} else {
f = &x[i]
x = f.Children
}
}
return
}
使用它(再次,繼續前面的例子):
path = "0.0.0"
f2 := getFoo(x, strings.Split(path, ".")...)
fmt.Printf("%+v\n", f2)
f2.A = 4
fmt.Printf("%+v\n", f2)
輸出(嘗試在Go Playground):
&{Children:[] A:3}
&{Children:[] A:4}
但要注意的是,如果我們只處理int
指數,它宣稱path
爲...string
(這是[]string
),int
切片會更有意義。
嵌套結構,你只需要做'A.B.C ...'。使用地圖,數組或片,您只需執行[a] [[x]] ...。沒有其他慣用的捷徑。 – Nadh
Python的列表解析提供了一種不同的方式來完成這樣的功能操作。 [這個回答解決了另一個涉及Go的列表解析的問題](http://stackoverflow.com/a/27848524/539810)值得一讀,因爲它解釋了它可能但不值得。 –