不可變數(shù)據(jù)類型對(duì)象是指,當(dāng)一個(gè)對(duì)象創(chuàng)建成功后,該變量就記錄了一個(gè)常量值在內(nèi)存中的地址.當(dāng)對(duì)該不可變對(duì)象進(jìn)行賦值時(shí),并沒有改變對(duì)象所代表的常量值,而是重新記錄了被賦值對(duì)象在內(nèi)存中的地址,
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供嵩明網(wǎng)站建設(shè)、嵩明做網(wǎng)站、嵩明網(wǎng)站設(shè)計(jì)、嵩明網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、嵩明企業(yè)網(wǎng)站模板建站服務(wù),十余年嵩明做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
可變數(shù)據(jù)類型對(duì)象可以理解成是一個(gè)容器,在這個(gè)容器中,可以承載多個(gè)相同或不同的數(shù)據(jù).并且,容器中的數(shù)據(jù)可以被替換修改等操作.
Python的可變型和不可變類型知道是什么了吧,如果學(xué)習(xí)Python不知道去哪里找學(xué)習(xí)資料,可以看黑馬程序員,有學(xué)習(xí)資料、視頻、技術(shù)等等!
Python中的列表是可變對(duì)象,對(duì)可變對(duì)象可以排序,比如說:L.sort()或者sorted(L),但在元祖數(shù)據(jù)結(jié)構(gòu)中,因?yàn)樵媸遣豢勺儗?duì)象,不會(huì)提供列表中的這些方法,方法就是先將元祖轉(zhuǎn)變?yōu)榱斜?,?duì)轉(zhuǎn)變后的列表排序后,再轉(zhuǎn)變回元祖。這樣就完成了元祖的排序。
【方法一:】借助于列表中的L.sort()方法
【方法二:】借助于列表中的sorted(L)方法
備注:
Python賦值操作或函數(shù)參數(shù)傳遞,傳遞的永遠(yuǎn)是對(duì)象引用(即內(nèi)存地址),而不是對(duì)象內(nèi)容。在Python中一切皆對(duì)象,對(duì)象又分為可變(mutable)和不可變(immutable)兩種類型。對(duì)象拷貝是指在內(nèi)存中創(chuàng)建新的對(duì)象,產(chǎn)生新的內(nèi)存地址。當(dāng)頂層對(duì)象和它的子元素對(duì)象全都是immutable不可變對(duì)象時(shí),不存在被拷貝,因?yàn)闆]有產(chǎn)生新對(duì)象。淺拷貝(Shallow Copy),拷貝頂層對(duì)象,但不會(huì)拷貝內(nèi)部的子元素對(duì)象。深拷貝(Deep Copy),遞歸拷貝頂層對(duì)象,以及它內(nèi)部的子元素對(duì)象。
Python中一切皆對(duì)象,對(duì)象就像一個(gè)塑料盒子, 里面裝的是數(shù)據(jù)。對(duì)象有不同類型,例如布爾型和整型,類型決定了可以對(duì)它進(jìn)行的操作?,F(xiàn)實(shí)生活中的"陶器"會(huì)暗含一些信息(例如它可能很重且易碎,注意不要掉到地上)。
對(duì)象的類型還決定了它裝著的數(shù)據(jù)是允許被修改的變量(可變的mutable)還是不可被修改的常量(不可變的immutable)。你可以把不可變對(duì)象想象成一個(gè)透明但封閉的盒子:你可以看到里面裝的數(shù)據(jù),但是無法改變它。類似地,可變對(duì)象就像一個(gè)開著口的盒子,你不僅可以看到里面的數(shù)據(jù),還可以拿出來修改它,但你無法改變這個(gè)盒子本身,即你無法改變對(duì)象的類型。
對(duì)象拷貝是指在內(nèi)存中創(chuàng)建新的對(duì)象,產(chǎn)生新的內(nèi)存地址。
淺拷貝(Shallow Copy),拷貝頂層對(duì)象,但不會(huì)拷貝內(nèi)部的子元素對(duì)象。
2.1.1. 頂層是mutable,子元素全是immutable
當(dāng)頂層對(duì)象是mutable可變對(duì)象,但是它的子元素對(duì)象全都是immutable不可變對(duì)象時(shí),如[1, 'world', 2]
① 創(chuàng)建列表對(duì)象并賦值給變量a
② 導(dǎo)入copy模塊,使用copy.copy()函數(shù)淺拷貝a,并賦值給變量b
③ 修改變量a的子元素a[0] = 3,由于整數(shù)是不可變對(duì)象,所以并不是修改1變?yōu)?,而是更改a[0]指向?qū)ο?
當(dāng)頂層對(duì)象是 mutable可變對(duì)象 ,但子元素也存在 mutable可變對(duì)象 時(shí),如 [1, 2, ['hello','world']]
① 淺拷貝 copy.copy() 只拷貝了頂層對(duì)象,沒有拷貝子元素對(duì)象['hello','world'],即a[2]和b[2]指向同一個(gè)列表對(duì)象
② 修改a[2][1] = 'china',則b[2][1] = 'china'
當(dāng)頂層對(duì)象是immutable不可變對(duì)象,同時(shí)它的子元素對(duì)象也全都是immutable不可變對(duì)象時(shí),如(1, 2, 3)
變量a與變量b指向的是同一個(gè)元組對(duì)象,沒有拷貝
當(dāng)頂層對(duì)象是immutable不可變對(duì)象時(shí),但子元素存在mutable可變對(duì)象時(shí),如(1, 2, ['hello','world'])
變量a與變量b指向的是相同的元組對(duì)象,并且a[2]與b[2]指向同一個(gè)列表,所以修改a[2][1]會(huì)影響b[2][1]
深拷貝(Deep Copy),遞歸拷貝頂層對(duì)象,以及它內(nèi)部的子元素對(duì)象
當(dāng)頂層對(duì)象是mutable可變對(duì)象,但是它的子元素對(duì)象全都是immutable不可變對(duì)象時(shí),如[1, 'world', 2]
變量a與變量b指向不同的列表對(duì)象,修改a[0]只是將列表a的第一個(gè)元素重新指向新對(duì)象,不會(huì)影響b[0]
當(dāng)頂層對(duì)象是mutable可變對(duì)象,但子元素也存在mutable可變對(duì)象時(shí),如[1, 2, ['hello','world']]
深拷貝既拷貝了頂層對(duì)象,又遞歸拷貝了子元素對(duì)象,所以a[2]與b[2]指向了兩個(gè)不同的列表對(duì)象(但是列表對(duì)象的子元素初始指定的字符串對(duì)象一樣),修改a[2][1] = 'china'后,它重新指向了新的字符串對(duì)象(內(nèi)存地址為140531581905808),不會(huì)影響到b[2][1]
當(dāng)頂層對(duì)象是immutable不可變對(duì)象,同時(shí)它的子元素對(duì)象也全都是immutable不可變對(duì)象時(shí),如(1, 2, 3)
變量a與變量b指向的是同一個(gè)元組對(duì)象,不存在拷貝
當(dāng)頂層對(duì)象是immutable不可變對(duì)象時(shí),但子元素存在mutable可變對(duì)象時(shí),如(1, 2, ['hello','world'])
變量a與變量b指向的是不同的元組對(duì)象,同時(shí)a[2]與b[2]指向不同的列表對(duì)象,所以修改a[2][1]不會(huì)影響b[2][1]
使用=是賦值,即將列表對(duì)象的引用也賦值給變量b,可以將列表對(duì)象想像成一個(gè)盒子,變量a相當(dāng)于這個(gè)盒子上的標(biāo)簽,執(zhí)行b = a后,相當(dāng)于再在這個(gè)盒子上貼上b標(biāo)簽,a和b實(shí)際上指向的是同一個(gè)對(duì)象。因此,無論我們是通過a還是通過b來修改列表的內(nèi)容,其結(jié)果都會(huì)作用于雙方。
b/c/d都是a的復(fù)制,它們都指向了不同的列表對(duì)象,但是沒有拷貝子元素,a[2]和b[2]/c[2]/d[2]指向同一個(gè)列表, 相當(dāng)于淺拷貝的效果
使用分片[:]操作,a和b其實(shí)是指向同一個(gè)元組,而且沒有拷貝子元素,a[2]和b[2]也指向同一個(gè)列表,相當(dāng)于淺拷貝的效果
同列表類似,可以使用字典的copy()函數(shù)或者轉(zhuǎn)換函數(shù)dict()
變量a與變量b/c指向不同的字典,但是沒有拷貝子元素,a['jobs']和b['jobs']/c['jobs']指定同一個(gè)列表, 相當(dāng)于淺拷貝的效果
同列表類似,可以使用集合的copy()函數(shù)或者轉(zhuǎn)換函數(shù)set()
變量a與變量b/c指向不同的集合,而集合的元素必須是hashable,所以修改集合a不會(huì)影響到b/c
python函數(shù)傳對(duì)象對(duì)性能有影響。在Python中,一切皆對(duì)象,Python參數(shù)傳遞采用的都是“傳對(duì)象引用”的方式。實(shí)際上,這種方式相當(dāng)于傳值和傳引用的一種綜合。如果函數(shù)收到的是一個(gè)可變對(duì)象(比如字典或者列表)的引用,就能修改對(duì)象的原始值,相當(dāng)于通過“傳引用”來傳遞對(duì)象。如果函數(shù)收到的是一個(gè)不可變對(duì)象(比如數(shù)字、字符或者元組)的引用,就不能直接修改原始對(duì)象,相當(dāng)于通過“傳值’來傳遞對(duì)象,此時(shí)如果想改變這些變量的值,可以將這些變量申明為全局變量。
在Python中對(duì)象的賦值(=)其實(shí)就是對(duì)象的引用。即:當(dāng)創(chuàng)建一個(gè)對(duì)象,把它賦值給另一個(gè)變量時(shí),python并沒有拷貝這個(gè)對(duì)象,只是拷貝了這個(gè)對(duì)象的引用而已。
Python中對(duì)象的拷貝分為:淺拷貝(copy)和深拷貝(deepcopy)。
淺拷貝:拷貝了最外圍的對(duì)象本身,內(nèi)部的元素都只是拷貝了一個(gè)引用而已。也就是,將原對(duì)象在內(nèi)存中引用地址拷貝過來,然后讓新的對(duì)象指向這個(gè)地址。可以使用“=”或列表自帶的copy()函數(shù)(如list.copy()),或使用copy模塊的copy()函數(shù)。
深拷貝:外圍和內(nèi)部元素都進(jìn)行了拷貝對(duì)象本身,而不是引用。即把對(duì)象復(fù)制一遍,并且該對(duì)象中引用的其他對(duì)象也同時(shí)復(fù)制,完全得到一個(gè)新的一模一樣的對(duì)象,對(duì)新對(duì)象里的值進(jìn)行修改不會(huì)影響原有對(duì)象,新對(duì)象和原對(duì)象完全分離開。深拷貝只能使用copy模塊中deepcopy()函數(shù),使用前要導(dǎo)入:from copy import deepcopy。
Python中對(duì)象分為不可變對(duì)象 、可變對(duì)象。
不可變對(duì)象:一旦創(chuàng)建就不可修改的對(duì)象,例如:字符串、元組、數(shù)字
可變對(duì)象:可以修改的對(duì)象,例如:列表、字典。
其中Python中的切片可以應(yīng)用于:列表、元組、字符串,但不能應(yīng)用于字典。
而深淺拷貝,可應(yīng)用于序列(列表、元組、字符串),也可應(yīng)用于字典。
其中不可變對(duì)象,不管是深拷貝還是淺拷貝,地址值在拷貝后的值都是一樣的。
以下以元組(不可變類型)為例
從上述示例可以看出:
不可變對(duì)象類型,沒有被拷貝的說法,即便是用深拷貝,查看id的話也是一樣的,如果對(duì)其重新賦值,也只是新創(chuàng)建一個(gè)對(duì)象,替換掉舊的而已。
所以不可變類型,不管是深拷貝還是淺拷貝,地址值和拷貝后的值都是一樣的。
以下以列表(可變類型)為例
第一種方法:使用=號(hào)淺拷貝
輸出結(jié)果:
第二種方法:使用copy淺拷貝
輸出結(jié)果:
第三種方法:使用deepcopy深拷貝
輸出結(jié)果:
從上述示例可以看出:
=淺拷貝:值相等,地址相等
copy淺拷貝:值相等,地址不相等
deepcopy深拷貝:值相等,地址不相等
總結(jié):
1,深淺拷貝都是對(duì)源對(duì)象的復(fù)制,占用不同的內(nèi)存空間。
2,不可變類型的對(duì)象,對(duì)于深淺拷貝毫無影響,最終的地址值和值都是相等的。
3,可變類型的對(duì)象,使用=淺拷貝時(shí), 值相等,地址相等,對(duì)新對(duì)象里的值進(jìn)行修改同時(shí)會(huì)影響原有對(duì)象;使用copy淺拷貝時(shí)值相等,地址不相等;使用deepcopy深拷貝時(shí)值相等,地址不相等??梢钥闯鲠槍?duì)可變類型copy淺拷貝和deepcopy深拷貝,對(duì)新對(duì)象里的值進(jìn)行修改不會(huì)影響原有對(duì)象。
Python中 可變(mutable)與不可變(immutable)的數(shù)據(jù)類型 讓新手很是頭痛。簡單的說,可變(mutable)意味著"可以被改動(dòng)",而不可變(immutable)的意思是“常量(constant)”。想把腦筋轉(zhuǎn)動(dòng)起來嗎?考慮下這個(gè)例子:
剛剛發(fā)生了什么?我們預(yù)期的不是那樣!
這不是一個(gè)bug。這是對(duì)象可變性(mutability)在作怪。 每當(dāng)你將一個(gè)變量賦值為另一個(gè)可變類型的變量時(shí),對(duì)這個(gè)數(shù)據(jù)的任意改動(dòng)會(huì)同時(shí)反映到這兩個(gè)變量上去。新變量只不過是老變量的一個(gè)別名而已。這個(gè)情況只是針對(duì)可變數(shù)據(jù)類型 。
啊哈!這次又沒有達(dá)到預(yù)期, 是列表的可變性在作怪 。在Python中當(dāng)函數(shù)被定義時(shí),默認(rèn)參數(shù)只會(huì)運(yùn)算一次,而不是每次被調(diào)用時(shí)都會(huì)重新運(yùn)算。你應(yīng)該永遠(yuǎn)不要定義可變類型的默認(rèn)參數(shù),除非你知道你正在做什么。你應(yīng)該像這樣做:
現(xiàn)在每當(dāng)你在調(diào)用這個(gè)函數(shù)不傳入target參數(shù)的時(shí)候,一個(gè)新的列表會(huì)被創(chuàng)建。舉個(gè)例子:
為什么會(huì)出現(xiàn)這樣?
a+=b
a=a+b
顯然,兩者是有區(qū)別的,而這種區(qū)別只出現(xiàn)在可變對(duì)象上(為什么是可變對(duì)象后面再說),是什么原因造成了兩者的區(qū)別呢?
+= 操作調(diào)用 __iadd__ 方法,沒有該方法時(shí),再嘗試調(diào)用 __add__ 方法
__iadd__ 方法 直接在原對(duì)象a1上進(jìn)行更新,該方法的返回值為None
+ 操作調(diào)用 __add__ 方法
__add__ 方法會(huì)返回一個(gè)新的對(duì)象,原對(duì)象不修改,因?yàn)檫@里 a1被重新賦值了,a1指向了一個(gè)新的對(duì)象,所以出現(xiàn)了文章開頭a1不等于a2的情況
為什么前面我說這種差異只會(huì)發(fā)生的 可變對(duì)象 身上?因?yàn)閷?duì)于不可變對(duì)象,根本沒有 __iadd__ 方法,所以 += 和 + 的效果是一樣的,因?yàn)檎{(diào)的都是 __add__ 方法