Python中的對象分為可變與不可變,有必要了解一下,這會影響到python對象的賦值與拷貝。而拷貝也有深淺之別。
不可變對象
簡單說就是某個對象存放在內存中,這塊內存中的值是不能改變的,變量指向這塊內存,如果要改變變量的值,只能再開辟一塊內存,放入新值,再讓變量指向新開辟的內存。
#定義三個變量 f=22 n=22 z=f print('f=%s,n=%s,z=%s' %(f,n,z)) print('f的地址:',id(f))#id用於獲取變量內存地址 print('n的地址:',id(n)) print('z的地址:',id(z)) print('注意:f、n、z的地址是一樣的。\n') n=9 #改變n的值 z=6 #改變z的值 print('f=%s,n=%s,z=%s' %(f,n,z)) print('f的地址:',id(f)) print('n的地址:',id(n)) print('z的地址:',id(z)) print('注意:f、n、z的地址不一樣了。')
執行結果:
f=22,n=22,z=22 f的地址:8790949926368 n的地址:8790949926368 z的地址:8790949926368 注意:f、n、z的地址是一樣的。 f=22,n=9,z=6 f的地址:8790949926368 n的地址:8790949925952 z的地址:8790949925856 注意:f、n、z的地址不一樣了。
上面的例子可以看出,當變量的值改變,它的地址也跟着改變了,就證明當變量的值改變時,python並沒有將原地址空間的內容改變,而是又重新分配了一塊內存空間,放上新值,然后將變量指向新開辟的空間地址。對於賦值,可以看出將 f 的值賦給 z,它們的地址是一樣的,但是當改變 z 的值,z的地址也跟着改變了,f 的值並沒有改變,因為它們的地址不一樣了。
可變對象
就是變量所指向的內存中的值是可以改變的。如果要修改變量值,不用開辟新的內存空間,直接在原地改變值,變量的地址不會改變。
下面的例子創建了兩個 list,值是相同的,打印地址可以看出它們的地址是不一樣的。再創建一個s3,將s1的值賦給s3,然后改變s3的值,發現s1的值也跟着變了,因為它們的指向的地址空間是一樣。
s1=[3,6,9] s2=[3,6,9] print(s1,s2) print('s1的地址:%s,s2的地址:%s' %(id(s1),id(s2))) #可以看出s1和s2的地址是不一樣的,雖然值一樣。 s3=s1#將s1的值賦給s3 print('s3的地址:%s' %(id(s3)))#s1和s3的地址是一樣的 s3.append(11)#在s3里面添加一個元素 print(s1,s3)#s1的值也跟着改變了
執行結果:
執行結果: [3, 6, 9] [3, 6, 9] s1的地址:92282952,s2的地址:92283976 s3的地址:92282952 [3, 6, 9, 11] [3, 6, 9, 11]
在python中,
-
數值類型(int 和 float)、字符串、元組都是不可變類型。
-
列表、字典、集合是可變類型。
淺拷貝與深拷貝
拷貝表面看就是復制一個一樣的對象,但它們最本質的區別就是復制出來的這個對象的地址是否和原對象的地址一樣,就是在內存中存放這兩個對象的位置是否發生了改變。由淺入深可分為三個層次(直接賦值、淺拷貝、深拷貝)。
以一個list為例子,演示三種不同層次的拷貝:首先構造一個list,這個list里面有兩種不同類型的元素,分別為不可變元素1,2,3和可變元素['FF','WW'],即ff=[1,2,3,['FF','WW']]。通過分析list對象即其里面包含元素的地址是否改變可以清晰看出拷貝的深淺之別。
第一層:直接賦值:構造一個 nn,直接將 ff 賦值給 nn,這種情況下 ff 和 nn 以及它們的元素所指向的地址是一樣的,改變任何一個對象,另一個也一樣改變,因為這個兩個對象及其元素指向的內存空間是一樣的,它們沒有自己的獨立內存空間。
#淺拷貝和深拷貝:直接賦值 ff=[1,2,3,['FF','WW']] nn=ff #將ff直接賦值給nn print('ff的地址:%s,ff[3]的地址:%s' %(id(ff),id(ff[3]))) print('nn的地址:%s,nn[3]的地址:%s' %(id(nn),id(nn[3]))) nn.append(4)#修改nn,在后面添加一個元素 nn[0]=99#修改nn元素值 nn[3].append('NN')#修改nn,在第三個list元素里添加一個元素 print(nn)#nn變了 print(ff)#ff也跟着變了
執行結果:
ff的地址:95127176,ff[3]的地址:95277576 nn的地址:95127176,nn[3]的地址:95277576 [99, 2, 3, ['FF', 'WW', 'NN'], 4] [99, 2, 3, ['FF', 'WW', 'NN'], 4]
第二層:淺拷貝:使用語句nn=copy.copy(ff)完成拷貝,通過觀察變量地址的變化來理解拷貝的不同。打印地址發現對象 nn 和原對象 ff 地址已經不一樣了,但元素的地址是一樣的,然后對 nn 做一下改變。
-
為nn添加一個元素,ff與nn地址不同,ff沒有受到影響。
-
將nn[1]的值改變,nn[1]為數值,是不可變對象,改變就是新開辟了內存。nn[1]的地址發生了變化,ff[1]的地址沒有改變,所以ff[1]的值沒有發生變化。
-
將nn[3]的值改變,nn[3]為列表,是可變對象,nn[3]的值變地址沒有變,因為ff[3]和nn[3]的地址相同,所以ff[3]的值也就改變了。
可見,淺拷貝復制的是元素的地址引用,如果元素是不可變類型,修改就更新了地址,和原對象的地址不同了,所以原對象不會受到影響,當元素是可變類型,修改沒有改變地址,這樣原對象也就跟着變化。
跟着變化。 #淺拷貝和深拷貝:淺拷貝 import copy ff=[1,2,3,['FF','WW']] nn=copy.copy(ff)#淺拷貝 print('ff的地址:',id(ff)) print('ff[1]的地址:',id(ff[1]))#ff里的不可變元素 print('ff[3]的地址:',id(ff[3]),'\n')#ff里的可變元素 print('nn的地址:',id(nn)) print('nn[1]的地址:',id(nn[1]))#nn里的不可變元素 print('nn[3]的地址:',id(nn[3]),'\n')#nn里的可變元素 nn.append(4)#修改nn,在后面添加一個元素 nn[1]=55 #修改不可變元素的值 nn[3].append('NN') #修改可變元素的值 print('ff:',ff) print('nn:',nn,'\n') print('ff的地址:%s,ff[1]的地址:%s,ff[3]的地址:%s' %(id(ff),id(ff[1]),id(ff[3]))) print('nn的地址:%s,nn[1]的地址:%s,nn[3]的地址:%s' %(id(nn),id(nn[1]),id(nn[3])))
執行結果:
ff的地址:96794888 ff[1]的地址:8790949925728 ff[3]的地址:96796168 nn的地址:96796040 nn[1]的地址:8790949925728 nn[3]的地址:96796168 ff: [1, 2, 3, ['FF', 'WW', 'NN']] nn: [1, 55, 3, ['FF', 'WW', 'NN'], 4] ff的地址:96794888,ff[1]的地址:8790949925728,ff[3]的地址:96796168 nn的地址:96796040,nn[1]的地址:8790949927424,nn[3]的地址:96796168
第三層:深拷貝:改變任何一個對象都對另一個沒有影響,它們是獨立的。通過觀察地址可以看出,對於不可變元素,重新創建一份似乎有點多余,反正修改就會刷新地址,所以ff[1]和nn[1]的地址還是一樣的,主要看可變對象ff[3]和nn[3],深拷貝后它們的地址已經不一樣了。
#淺拷貝和深拷貝:深拷貝 import copy ff=[1,2,3,['FF','WW']] nn=copy.deepcopy(ff)#深拷貝 print('ff的地址:',id(ff)) print('ff[1]的地址:',id(ff[1]))#ff里的不可變元素 print('ff[3]的地址:',id(ff[3]),'\n')#ff里的可變元素 print('nn的地址:',id(nn)) print('nn[1]的地址:',id(nn[1]))#nn里的不可變元素 print('nn[3]的地址:',id(nn[3]),'\n')#nn里的可變元素 nn.append(4) #修改nn,在后面添加一個元素 nn[1]=55 #修改不可變元素的值 nn[3].append('NN') #修改可變元素的值 print('ff:',ff) print('nn:',nn,'\n') print('ff的地址:%s,ff[1]的地址:%s,ff[3]的地址:%s' %(id(ff),id(ff[1]),id(ff[3]))) print('nn的地址:%s,nn[1]的地址:%s,nn[3]的地址:%s' %(id(nn),id(nn[1]),id(nn[3])))
執行結果:
ff的地址:93244616 ff[1]的地址:8791030272864 ff[3]的地址:93241416 nn的地址:93244552 nn[1]的地址:8791030272864 nn[3]的地址:93244744 ff: [1, 2, 3, ['FF', 'WW']] nn: [1, 55, 3, ['FF', 'WW', 'NN'], 4] ff的地址:93244616,ff[1]的地址:8791030272864,ff[3]的地址:93241416 nn的地址:93244552,nn[1]的地址:8791030274560,nn[3]的地址:93244744
通俗一點:
-
直接賦值:兩個對象你中有我,我中有你,同住一間房。
-
淺拷貝:兩個對象分居中,沒離婚,藕斷絲連,有部分個人空間。
-
深拷貝:兩個對象徹底離婚了,都不在一個城市了,各住各家,互不影響。
-------------------------- END --------------------------