自底向上:從可變對(duì)象、不可變對(duì)象到深淺拷貝再到數(shù)據(jù)結(jié)構(gòu)
一、不可變對(duì)象和可變對(duì)象**
Python 在 heap 中分配的對(duì)象分成兩類:可變對(duì)象和不可變對(duì)象。所謂可變對(duì)象是指,對(duì)象的內(nèi)容是可變的,例如 list。而不可變的對(duì)象則相反,表示其內(nèi)容不可變。
不可變對(duì)象 :int,string,float,tuple -- 可理解為C中,該參數(shù)為值傳遞
可變對(duì)象 :list,dictionary -- 可理解為C中,該參數(shù)為指針傳遞
不可變對(duì)象
由于Python中的變量存放的是對(duì)象引用,所以對(duì)于不可變對(duì)象而言,盡管對(duì)象本身不可變,但變量的對(duì)象引用是可變的。運(yùn)用這樣的機(jī)制,有時(shí)候會(huì)讓人產(chǎn)生糊涂,似乎可變對(duì)象變化了。如下面的代碼:
for i in range(10):
print(id(i))
outputs:
2221403367696
2221403367728
2221403367760
2221403367792
2221403367824
2221403367856
2221403367888
2221403367920
2221403367952
2221403367984

從上面得知,不可變的對(duì)象的特征沒(méi)有變,依然是不可變對(duì)象,變的只是創(chuàng)建了新對(duì)象,改變了變量的對(duì)象引用。
對(duì)于可變對(duì)象
其對(duì)象的內(nèi)容是可以變化的。當(dāng)對(duì)象的內(nèi)容發(fā)生變化時(shí),變量的對(duì)象引用是不會(huì)變化的。如下面的例子。
m = [1,2,3]
print(id(m))
m += [4]
print(id(m))
outputs:
2221492412544
2221492412544
對(duì)于可變對(duì)象 list這種進(jìn)行操作時(shí),相當(dāng)于修改對(duì)象中某個(gè)屬性,所以不會(huì)改變地址 python都是將對(duì)象的引用(內(nèi)存地址)賦值給變量的。
現(xiàn)在問(wèn)題來(lái)了
def myfunc(l):
l.append(1) # 列表加1
print(l)
l = [1,2,3]
myfunc(l) # [1,2,3,1]
print(l) # [1,2,3,1]
可變對(duì)象l送進(jìn)函數(shù)中操作后,對(duì)函數(shù)外面的也起作用,不可變對(duì)象則相反。因?yàn)楹瘮?shù)直接對(duì)可變對(duì)象的地址的值進(jìn)行了修改。該操作對(duì)于類初始化傳值有一樣的效果。
只有搞懂可變對(duì)象和不可變對(duì)象之后,才能理解下面的深拷貝與淺拷貝。
二、深拷貝和淺拷貝
淺拷貝
- 淺拷貝會(huì)創(chuàng)建一個(gè)新的容器對(duì)象(compound object)
- 對(duì)于對(duì)象中的元素,淺拷貝就只會(huì)使用原始元素的引用(內(nèi)存地址)
深拷貝
- 深拷貝和淺拷貝一樣,都會(huì)創(chuàng)建一個(gè)新的容器對(duì)象(compound object)
- 和淺拷貝的不同點(diǎn)在于,深拷貝對(duì)于對(duì)象中的元素,深拷貝都會(huì)重新生成一個(gè)新的對(duì)象
更進(jìn)一步,由于淺拷貝復(fù)制的是元素的地址引用,如果元素是不可變類型,修改就更新了地址,和原對(duì)象的地址不同了,所以原對(duì)象不會(huì)受到影響,當(dāng)元素是可變類型,修改沒(méi)有改變地址,這樣原對(duì)象也就跟著變化。
對(duì)于深拷貝而言,改變?nèi)魏我粋€(gè)對(duì)象都對(duì)另一個(gè)沒(méi)有影響,它們是獨(dú)立的。無(wú)論是不可變對(duì)象,還是可變對(duì)象,深拷貝后它們的地址已經(jīng)不一樣了。
三、哈希表中的可哈希和不可哈希對(duì)象
了解了上文之后,在python中使用哈希表(散列表)數(shù)據(jù)結(jié)構(gòu)時(shí),又有可哈希和不可哈希對(duì)象之分,其實(shí)這里就是可變對(duì)象和不可變對(duì)象。為什么呢?
對(duì)于不可變類型而言,不同的值意味著不同的內(nèi)存,相同的值存儲(chǔ)在相同的內(nèi)存,如果將我們的不可變對(duì)象理解成哈希表中的 Key,將內(nèi)存理解為經(jīng)過(guò)哈希運(yùn)算的哈希值 Value,這不正好滿足哈希表的性質(zhì)嘛。
對(duì)于可變對(duì)象而言,比如一個(gè)列表,更改列表的值,但是對(duì)象的地址本身是不變的,也就是說(shuō)不同的 Key,映射到了相同的 Value,這顯然是不符合哈希值的特性的,即出現(xiàn)了哈希運(yùn)算里面的沖突。
所以在python中使用set()、dictionary()時(shí)鍵值都必須是不可變對(duì)象。
參考文章:Python基礎(chǔ):Python可變對(duì)象和不可變對(duì)象
posted on 2022-04-13 10:56 看這個(gè)就夠了 閱讀(124) 評(píng)論(0) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)