python一切皆對(duì)象,命名皆引用
引用的理解
Python中一切皆對(duì)象,數(shù)值、字符串、函數(shù)、列表、類、文件等都是對(duì)象,加載到內(nèi)存中時(shí)會(huì)給這些對(duì)象分配一些內(nèi)存資源,可以通過id()函數(shù)來表示它們的內(nèi)存地址。
Python中的賦值操作
python的一切都是圍繞變量名和對(duì)象進(jìn)行的。
Python中一切變量名都是引用,其賦值操作就是在給對(duì)象創(chuàng)建新的引用名,Python中普通的賦值操作就相當(dāng)于C++中的引用操作。
比如代碼:a = 1。
在C++中是表示將a這個(gè)變量所在內(nèi)存的內(nèi)容改為1。
在python中則表示讓a指向?qū)ο?。
再比如:a += 1。
在C++中是表示將a這個(gè)變量所在內(nèi)存的內(nèi)容加上1。
在python中則表示讓a指向2(加1后的值)這個(gè)對(duì)象。
再比如:a[0] = 100。
在C++中同樣表示修改a[0]所在內(nèi)存的內(nèi)容。
在python中則表示讓a[100]指向100這個(gè)對(duì)象,或給100這個(gè)對(duì)象命名為a[100]。
所以python中賦值的本質(zhì)就是引用的指向變來變?nèi)?/strong>。
對(duì)于賦值操作,可以分為兩種情況:
變量名和變量名之間進(jìn)行,這種情況稱為間接引用。
變量名和對(duì)象之間進(jìn)行,這種情況稱為直接引用。
賦值后,新引用名所指向的對(duì)象有如下規(guī)則(對(duì)形參和實(shí)參都適用):
?通過間接引用賦值,無論什么數(shù)據(jù)類型,都指向同一個(gè)內(nèi)存地址,即同一個(gè)對(duì)象。
對(duì)于直接引用有:如果是可變數(shù)據(jù)類型(list、set、dict、可變tuple),會(huì)分別開辟內(nèi)存,建立新的對(duì)象;對(duì)于不可變數(shù)據(jù)類型(含不可變tuple),則不會(huì)開辟新內(nèi)存,比如常量等在內(nèi)存中永遠(yuǎn)只有唯一的一份。
Python中的容器&深拷貝和淺拷貝
既然Python中一切命名都是引用,那么python容器的本質(zhì)就是引用的集合。
對(duì)于容器類型的對(duì)象存在一個(gè)深拷貝和淺拷貝的問題
淺拷貝只涉及到創(chuàng)建新的父對(duì)象的引用名,不涉及任何創(chuàng)建新對(duì)象及創(chuàng)建新的子對(duì)象引用名的操作。
深拷貝會(huì)完全創(chuàng)建出一個(gè)全新的對(duì)象,新的引用名會(huì)指向新的對(duì)象。注意對(duì)于不可變類型是不會(huì)創(chuàng)建新對(duì)象的,新引用指向的仍是原始對(duì)象。
例子1:

b直接引用a,兩者指向同一對(duì)象,操作a等于操作b
a == ?[1,2, 3, 4, ['a', 'b', 'c'], 5]
b == ?[1,2, 3, 4, ['a', 'b', 'c'], 5]
c是a的淺拷貝,父對(duì)象索引名都是新的引用名稱,如a[4]和c[4]是兩個(gè)不同的引用名,但a[4]和c[4]指向同一個(gè)對(duì)象,子對(duì)象引用a[0][0]和c[0][0]是同一個(gè)引用名。
c == ?[1,2, 3, 4, ['a', 'b', 'c']]
d=[1, 2, 3, 4, ['a', 'b']]?
例子2:

將kvps['1']指向?qū)ο?,因?yàn)閗vps['1']和theCopy['1']是兩個(gè)不同的引用名,所以修改kvps['1']的指向并不影響theCopy['1']的指向,所以答案是6.
例子3:

間接引用,list['1']和list2['1']是同一個(gè)引用名,沒有創(chuàng)建新的引用名,所以結(jié)果是10。
例子4:

淺拷貝創(chuàng)建了新的引用名,所以修改tmp['one']的指向不會(huì)影響原引用,結(jié)果是:
{'one': 1, 'two': 2, 'three': 3},{'one': 'abc', 'two': 2, 'three': 3}