平時寫得多的是python,最近看了一點go,今天碰到了一個問題,和大家分享一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main import "fmt" type student struct { Name string Age int } func pase_student() { m : = make( map [string] * student) stus : = []student{ {Name: "zhou" , Age: 24 }, {Name: "li" , Age: 23 }, {Name: "wang" , Age: 22 }, } for _, stu : = range stus { m[stu.Name] = &stu } fmt.Println(m[ "zhou" ].Name) } func main() { pase_student() } |
代碼很簡單,大家可以思考一下會打印出什么。
time.sleep(60) # 思考
結果是 wang !,驚喜不驚喜!遍歷賦值啊同學們,這么簡單的操作都能出幺蛾子,WTF!
為什么是 wang 呢?
你tm給我
解釋解釋
什么是驚喜
:
for循環的時候,變量stu的指針是不變的,每次循環僅僅是對student結構體的 值 拷貝,上面的for循環和下面是一樣的:
1
2
3
4
|
var stu student for _, stu = range stus { m[stu.Name] = &stu } |
所以 &stu 自始至終都是一個地址,變化的是這個地址上存儲的值。 &stu 最終存儲的值是 student{Name: "wang", Age: 22} 結構體,所以拿出來的是 wang 。
可以將 m 打出來看一下:
map[zhou:0xc42000a260 li:0xc42000a260 wang:0xc42000a260]
驗證了我們上面的想法,大家的value都是同一個地址。
看到這里,如果是一個日常寫c,c++等強類型語言的同學可能會說,神經病啊!這有什么好說的!不就是這樣的嗎!請原諒我,我日常寫python的 [捂臉]。
從上面的例子可以看出來,在go中,變量名是 存儲地址的名字 。它在編譯時綁定已經完成,運行時是不可以改變的,你只能改變地址中存儲的值。
而在python中,變量是對象的名字,運行時變量可以綁定到任意的對象上。如下所示:
1
2
3
4
5
6
7
8
9
|
In [ 4 ]: a = 123456 In [ 5 ]: id (a) Out[ 5 ]: 4426596208 In [ 6 ]: a = 1234567 In [ 7 ]: id (a) Out[ 7 ]: 4426592592 |
注意:由于python對int類型實現了 小整數對象池 ,不要用 0-255 的整數做實驗,不然你得到id會是一樣的。
也就是說,當你循環一個list的時候,每次得到的是不同對象,變量指向了不同的地址:
1
2
3
4
5
6
|
In [ 9 ]: for i in [ 2222 , 2223 , 2224 ]: ...: print ( id (i)) ...: 4426596208 4426592336 4426596080 |
上面這段代碼,python為我們創建了3個 PyIntObject , i 只是他們的名字。而在go中,可以認為只有一個object,值變化了3次。
python中說的 賦值就是建立一個對象的引用 ,是實話。
原文鏈接:http://michaelyou.github.io/2017/08/30/go%E5%92%8Cpython%E5%8F%98%E9%87%8F%E8%B5%8B%E5%80%BC%E7%9A%84%E4%B8%80%E4%B8%AA%E5%B0%8F%E9%97%AE%E9%A2%98/