python,可變對象,不可變對象,深拷貝,淺拷貝。


學習整理,若有問題,歡迎指正。

python 可變對象,不可變對象

  1. 可變對象

該對象所指定的內存地址上面的值可以被改變,變量被改變后,其所指向的內存地址上面的值,直接被改變,沒有發生復制行為,也沒有發生開辟新的內存地址行為。

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初始值一樣的。

 當改變 復雜子對象中的元素時,淺拷貝值發生了變化; 當改變的值不是復雜子對象,淺拷貝的值沒有發生變化。因為 淺拷貝 ,復雜子對象的保存方式是 作為 引用 方式存儲的,所以修改 淺拷貝的值 和原來的值都可以 改變 復雜子對象的值。

圖解:

 

  • 深度拷貝
    • 即將被復制對象完全再復制一遍作為獨立的新個體單獨存在。所以改變原有被復制對象不會對已經復制出來的新對象產生影響
    • 上圖中已經有說明了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM