學習整理,若有問題,歡迎指正。
python 可變對象,不可變對象
- 可變對象
該對象所指定的內存地址上面的值可以被改變,變量被改變后,其所指向的內存地址上面的值,直接被改變,沒有發生復制行為,也沒有發生開辟新的內存地址行為。
python可變對象有,列表,字典,set集合
列如:
a = ['1','2','3'] print(id(a)) 2275736586376 a.append('1') print(a) ['1', '2', '3', '1'] print(id(a)) 2275736586376
我們可以看到,在向 a 列表內追加數據的時候,列表 a 前后的id沒有發生變化,所以內存地址內,沒有開辟新的空間,而是直接在原指定內存地址上面修改的。
圖解:
2. 不可變對象
該對象所指定內存中的值不可以被改變,在改變某個對象的值的時候,由於其內存中的值不可以被改變,所以,會把原來的值復制一份再進行改變,這樣就會計算機會開辟
一段新的內存空間來存儲新的值,python 不可變對象有 int str float number,tuple,None
例如:
c = 'Hello' print(id(c)) 2237718097848 print(c) Hello c = c + 'World' print(id(c)) 2237720838960 print(c) HelloWorld
我們可以看到,c變量在追加的時候,兩個id是不一樣的,所以內存地址會發生變化,此變化為,將c變量所指向的內存,復制一份,然后再做更改。
圖解:
此時,c的內存是指向 HelloWorld的,若Hello內存,長時間沒有變量指定,則垃圾回收機制會自動回收的。
3. 等於賦值,並不會產生獨立的對象單獨存在,而是給原來的數據塊打上一個新的標簽,其中一個標簽值被改變的時候,另一個標簽也隨之改變。
alist = [1,2,3,['a','b','c']] b = alist #此處id相同,所以指向同以內存地址 print(id(alist)) 2030874445768 print(id(b)) 2030874445768 #向alist內追加元素 alist.append(5) print(alist) [1, 2, 3, ['a', 'b', 'c'], 5] print(b) [1, 2, 3, ['a', 'b', 'c'], 5] #向alist內的列表追加元素 alist[3].append("d") print(alist) [1, 2, 3, ['a', 'b', 'c', 'd'], 5] print(b) [1, 2, 3, ['a', 'b', 'c', 'd'], 5] print(id(alist)) 2030874445768 print(id(b)) 2030874445768
由上面代碼看出,alist所指向 [1,2,3,['a','b','c']] 內存地址,b 也是指定的[1,2,3,['a','b','c']]內存地址(id相同),所以a.append(5)后,alist變化,b也變化
圖解:
由上圖可以看出,無論alist做什么操作,b的內存地址都是指向alist的,所以alist的任何變化,b都會跟着變化。
4. 淺復制,淺復制分為兩種情況
- 第一種情況,淺復制的值是不可變對象(數值,字符串,元組)時,與等值復制的是一樣的,對象的id值與淺復制對象的id是一樣的。
import copy a = 'abcde' b = a print(id(a)) 2030878234584 print(id(b)) 2030878234584 c = copy.copy(a) print(id(c)) 2030878234584 #當改變a的值 a = a + 'fg' print(id(a)) 2030878515304 #由於字符串為不可變對象,所以id(a)會發生變化,但是b,c不會發生變化,還是指向原有內存 print(id(b)) 2030878234584 print(id(c)) 2030878234584
由上面代碼可以看出 b = a 其實b,a是指向同一塊內存的(id)相同,c=copy.copy(a)也是和a,b指向同一塊內存的(id相同),當a發生變化的時候,a會復制原有值,開辟一塊新的內存
出來,此時,c與b還是指向原有內存(觀察id)
圖解:
- 當淺復制的值是可變對象(列表和元組)時會產生一個“不是那么獨立的對象”存在。有兩種情況:
- 復制的 對象中無 復雜 子對象,原來值的改變並不會影響淺復制的值,同時淺復制的值改變也並不會影響原來的值。原來值的id值與淺復制原來的值不同。
當淺復制的值是可變對象(列表,字典)時,改變的值不是 復雜子對象 代碼如下:
list1 = ['1','2','3'] print(id(list1)) 2030878577608 list2 = list1 print(id(list2)) 2030878577608 list3 = list1.copy() print(id(list2)) 2030878577608 #上面三者id都一致 #然后在list1后面append一個新的元素,查看變化情況 list1.append('4') print(list1) ['1', '2', '3', '4', '4'] print(id(list1)) 2030878577608 print(list2) ['1', '2', '3', '4', '4'] print(id(list2)) 2030878577608 print(list3) ['1', '2', '3'] print(id(list3)) 2030878531400 #list3的值與list1,list2的值,不一樣,且id也不一樣了
當list2 = list1 的時候,完全指向同一塊內存空間,list3 = list1.copy()的時候,list3也指向同一塊內存空間,當list1發生變化的時候,list2與list1開辟出一塊新的空間用來改變,list3還指向原有空間(或者相反)
圖解:
- 當淺復制的值是可變對象(列表,字典)時,改變的值是 復雜子對象 代碼如下:
l1 = [1,2,['a','b']] l2 = l1 l3 = copy.copy(l2) l4 = copy.deepcopy(l3) print(id(l1)) 2030878621960 print(id(l2)) 2030878621960 print(id(l3)) 2030878622664 print(id(l4)) 2030878622728 #id可以看出,l1,l2的id沒有改變,但是,l3,l4的id發生了變化 l1[2].append('a') print(id(l1)) 2030878621960 print(l1) [1, 2, ['a', 'b', 'a']] print(l3) [1, 2, ['a', 'b', 'a']] print(l4) [1, 2, ['a', 'b']] #l1[2].append('a')是改變l1內的可變列表的值,l1的id沒有變化,值有變化,l3的值有變化,但是l4的值沒有變化。 l1.append(3) print(id(l1)) 2030878621960 print(l1) [1, 2, ['a', 'b', 'a'], 3] print(l2) [1, 2, ['a', 'b', 'a'], 3] print(l3) [1, 2, ['a', 'b', 'a']] print(l4) [1, 2, ['a', 'b']] #l1.append(3)是改變l1的值,l1id沒有變化,l3的值沒有被改變(還是和上一步的值一樣),l4與l1初始值一樣的。
當改變 復雜子對象中的元素時,淺拷貝值發生了變化; 當改變的值不是復雜子對象,淺拷貝的值沒有發生變化。因為 淺拷貝 ,復雜子對象的保存方式是 作為 引用 方式存儲的,所以修改 淺拷貝的值 和原來的值都可以 改變 復雜子對象的值。
圖解:
- 深度拷貝
- 即將被復制對象完全再復制一遍作為獨立的新個體單獨存在。所以改變原有被復制對象不會對已經復制出來的新對象產生影響
- 上圖中已經有說明了。