Python走過的坑,可變不可變資料型別
Python走過的坑,可變不可變資料型別
Python標準資料型別
- Number(數字)
- String(字串)
- Dictionary(字典)
- List(列表)
- Tuple(元組)
- Set(集合)
Python不可變資料型別(一個蘿蔔一個坑)
比如數字,字串,元組。這是什麼意思呢,show your code:
a = 1
print("變數a在記憶體當中的地址":,id (a))
a += 1
print("變數a在記憶體當中的地址":,id(a))
結果為
變數a在記憶體當中的地址 1683713088
變數a在記憶體當中的地址 1683713120
可以看到,變數a在記憶體當中的地址發生了變化,多了32個bit,也就是4個位元組,這裡說句題外話,在python中int代表的是長整型,沒有 python2 中的 Long。
換句話說,一個數字對應一個記憶體地址,數字變,地址變,一個蘿蔔一個坑。
還有一個要注意的地方。
a = 1
print("變數a在記憶體當中的地址":,id(a))
b = 1
print("變數a在記憶體當中的地址" :,id(b))
結果為:
變數a在記憶體當中的地址 1683713088
變數a在記憶體當中的地址 1683713088
一模一樣的,這說明了什麼,說明了變數a和b指向了同一塊內容,其實就是同一個物件的不同引用,也就是同一個人的不同名字而已。或者還是拿蘿蔔來說。一個坑裡,只能長一個蘿蔔,中國人叫蘿蔔,老外管它叫turnip(蘿蔔的英文),這下懂了吧。
可變資料型別(一塊地好多蘿蔔)
典型的就是列表,這個可吃了不少虧。
來吧
a = [1,2,3]
print("變數a在記憶體當中的地址":,id(a))
a.append(4)
print("變數a在記憶體當中的地址" :,id(a))
結果為
變數a在記憶體當中的地址 2635029663240
變數a在記憶體當中的地址 2635029663240
可以看到,一毛一樣啊。這就相當於在原來的地裡又長了一個蘿蔔,當然也許是又長了白菜,土豆什麼的內建的資料型別,或者是自定義的資料型別等等。
比如a = [1,2,3,“白菜”]
來一張圖更形象一點。一個矩形的土地,長了3個蘿蔔。
然後又扔過去一個白菜。
地還是那塊地(記憶體地址並沒有發生改變),但是其中元素增加了。
總結為:元素變,地址不變,同一塊地可以種好多蘿蔔,這就是可變資料型別。
這裡也有一個值得注意的問題:
come on code!
a = [1,2,[3,4]]
print('a = :',a)
b = a
b[2] = [5,6]
print('a = ',a)
結果為:
a = [1, 2,[3, 4]]
a = [1, 2, [5, 6]]
這裡a和b所指的是同一個東西,也就是說,a和b都是[1,2,[3,4]]這個列表的引用,也就是外號。一般我們在記憶體中建立了一個列表,有兩種情況:
- 需要建立一個此列表的副本,一個一模一樣的列表,只不過在記憶體中多開闢了一塊空間來。這樣做的好處是,對副本所做修改不會影響到原來的列表,因為有時候我們需要原列表與修改後的列表進行對比。需要建立一個此列表的副本,一個一模一樣的列表,只不過在記憶體中多開闢了一塊空間來。這樣做的好處是,對副本所做修改不會影響到原來的列表,因為有時候我們需要原列表與修改後的列表進行對比。比如:
a = [1,2,[3,4]]
b = a.copy()
a[2][-1] = 'x'
print(a)#結果為[1, 2, [3, 'x']]
print(b)#結果為[1, 2, [3, 'x']]
咦,不對呀,我明明建立了一個原列表的副本啊,怎麼對a修改,b也會跟著修改呢?這是因為淺複製,深複製的問題,如果列表只有一層,那麼用淺複製足夠了。但是我們做專案的時候,動不動就好幾層的list,因此,必須採用深複製。下面是例項程式碼:
import copy
a = [1,2,[3,4]]
b = copy.deepcopy(a)
a[2][-1] = 'x'
print(a)#結果為[1, 2, [3, 'x']]
print(b)#結果為[1, 2, [3, 4]]
這樣,就可以完完全全地複製一個列表了。有個部落格對這種情況說的挺好的,
開啟連結,有句話是這麼說的,因為淺複製 ,複雜子物件的儲存方式是作為引用方式儲存的,所以修改淺複製的值和原來的值都可以改變複雜子物件的值。
2. 只需另外再建立一個對列表物件的引用即可,這種情況不需要原列表與修改後的列表進行對比,當列表比較大,又使用不到以前版本的列表時,推薦使用這種方式,節省記憶體。比如
a = [1,2,3]
b = a