
寫(xiě)在前面
python中的“變量”是一個(gè)可以被賦值的廣義對(duì)象,更確切地說(shuō)是“引用”,它僅僅只是一個(gè)標(biāo)簽,其作用就是將它與內(nèi)存中的某個(gè)實(shí)際對(duì)象(狹義對(duì)象)相聯(lián)系,從而通過(guò)操作這個(gè)標(biāo)簽來(lái)操作實(shí)際對(duì)象。
一個(gè)變量不能孤立存在,創(chuàng)立之時(shí)就必須將其指向內(nèi)存中的某個(gè)實(shí)際對(duì)象(即用‘=’操作),此時(shí)變量的類(lèi)型、內(nèi)存地址、值就是它指向?qū)ο蟮念?lèi)型、內(nèi)存地址、值。
一個(gè)變量本身不屬于任何一種數(shù)據(jù)類(lèi)型,python不會(huì)為一個(gè)變量分配內(nèi)存,同一個(gè)變量可以指向不同類(lèi)型的對(duì)象(例a=300,a='a', a=[300,'a'])。
Python通過(guò)引用計(jì)數(shù)機(jī)制實(shí)現(xiàn)自動(dòng)垃圾回收功能,Python內(nèi)存中的每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)器,用于統(tǒng)計(jì)該對(duì)象還被哪些變量引用著。多一次引用,計(jì)數(shù)多一次,少一次引用,計(jì)數(shù)少一次。當(dāng)計(jì)數(shù)為零時(shí),Python會(huì)將該對(duì)象銷(xiāo)毀,回收內(nèi)存。
賦值操作,表面上是將“=”右邊的量的值賦給左邊的變量,但本質(zhì)上是將右邊的量對(duì)應(yīng)的內(nèi)存對(duì)象與左邊的變量相聯(lián)系的過(guò)程。見(jiàn)下例:
>>>a = 300
該賦值語(yǔ)句做了三件事:1)開(kāi)辟了一塊內(nèi)存(可用id(a)查看地址);2)將該內(nèi)存的值設(shè)為300;3)將變量a指向該內(nèi)存。在重新將a指向其他對(duì)象之前,a就一直指向該內(nèi)存,也就一直代表整數(shù)300。這里內(nèi)存中的300是狹義“對(duì)象”,是個(gè)實(shí)實(shí)在在存在的東西。
在賦值語(yǔ)句中,通常有兩種情況:
一種是將狹義對(duì)象賦值給變量,形如:“變量=對(duì)象”,例如a = 300、a = 'python cgx'、a = [1,2,3]、a = (1,2,3)等,這幾個(gè)賦值語(yǔ)句右邊都是實(shí)實(shí)在在的對(duì)象,是存在于內(nèi)存中的。
另一種是將變量賦值給變量,形如:“變量=變量”,例如b=a,右邊是一個(gè)已經(jīng)存在的變量,注意它只是個(gè)引用,是個(gè)標(biāo)簽,不是具體對(duì)象,但它肯定已經(jīng)指向某個(gè)真實(shí)的對(duì)象了,否則a是不能存在的。
這兩種情況在python中有很大的不同,下面分別詳細(xì)討論。
(一)變量=對(duì)象,對(duì)象賦值給變量的情況
對(duì)于這種形式,我們也分兩種情況討論:
(1)賦值時(shí),待賦值“對(duì)象”在內(nèi)存中不存在
這種情況非常簡(jiǎn)單,遵循三步驟:開(kāi)辟內(nèi)存,指定內(nèi)存對(duì)應(yīng)的值,將其與變量相聯(lián)系。如下:
>>>a = 300 #int
>>>b = 'python cgx' #string
>>>c = [1,2,3] #list
>>>d = (1,2,3) #tuple
>>>e = {'age':25} #字典
(2)賦值時(shí),值相同的“對(duì)象”在內(nèi)存中已經(jīng)存在至少一個(gè)
對(duì)于這種情況稍微復(fù)雜,必須根據(jù)具體數(shù)據(jù)類(lèi)型分別進(jìn)行討論:
1)int,整型
>>>a = 1
>>>b = 1
>>>id(a) #1598778384
>>>id(b) #1598778384
上例中a和b的指向的內(nèi)存地址都是1598778384,說(shuō)明在執(zhí)行b=1時(shí),并沒(méi)有重新開(kāi)辟內(nèi)存,而是將變量b也指向已經(jīng)存在的對(duì)象1上。但千萬(wàn)不要認(rèn)為所有的int情況都一樣,比如:
>>>a = 300
>>>b = 300
>>>id(a) #96391536
>>>id(b) #96391664
根據(jù)前一個(gè)例子的經(jīng)驗(yàn),這里a和b兩個(gè)變量都指向?qū)ο?00,因此id(a)和id(b)應(yīng)該相同才對(duì),然而實(shí)際上兩者卻是不同的,這是什么情況?到底哪些整數(shù)會(huì)相同,哪些會(huì)不同呢?為了解決這一問(wèn)題,我們做如下實(shí)驗(yàn)
>>>for i in range(-1000,1000):
a = i #直接將i賦給a
b = int(i.__str__()) #先將i轉(zhuǎn)化為字符串再轉(zhuǎn)化為int,以區(qū)別將同值但新的i賦給b
if a is b: #判斷a和b的地址是否相同
print(i)
-5
-4
-3
……
255
256
上述代碼的目的是查看到底哪些整數(shù)會(huì)共享內(nèi)存,從結(jié)果可以看出從-5開(kāi)始到256結(jié)束,共262個(gè)整數(shù)會(huì)共享內(nèi)存。這真是神奇的存在,特別是為啥負(fù)整數(shù)只到-5。這個(gè)問(wèn)題暫時(shí)先不去深究。
python之所以這么干,是因?yàn)檫@些小的整數(shù)在編程時(shí)出現(xiàn)的頻率非常高,如果不停地都生成銷(xiāo)毀生成銷(xiāo)毀,會(huì)使計(jì)算效率降低,為了減少這種操作,干脆就將所有重復(fù)創(chuàng)建小整數(shù)的情況都統(tǒng)統(tǒng)指向同一個(gè)已經(jīng)存在的小整數(shù)上,只要還有一個(gè)變量連接到該小整數(shù),他就不會(huì)被銷(xiāo)毀。而且int數(shù)據(jù)是不可變對(duì)象,因此該小整數(shù)一旦在內(nèi)存中被創(chuàng)建,就會(huì)一直存在,直到?jīng)]有變量指向他才被銷(xiāo)毀。
對(duì)于浮點(diǎn)數(shù)則不存在這種情況。
2)string,字符串型
字符串的情況跟int有點(diǎn)類(lèi)似,對(duì)于短字符串(沒(méi)有空格)和空字符串('')而言,共享內(nèi)存;對(duì)于長(zhǎng)字符串(有空格)而言,不共享內(nèi)存。
>>>a = '' #空字符串
>>>b = ''
>>>id(a) #3504816
>>>id(b) #3504816
>>>a = 'sssssssssssssssssssssssssssssssss' #沒(méi)空格,短字符串
>>>b = 'sssssssssssssssssssssssssssssssss'
>>>id(a) #96397424
>>>id(b) #96397424
>>>a = 's s' #有空格,長(zhǎng)字符串
>>>b = 's s'
>>>id(a) #96387624
>>>id(b) #96388856
3)list,列表型
對(duì)于列表而言,比較簡(jiǎn)單,每次都開(kāi)辟新的內(nèi)存,生成新的對(duì)象,包括空列表[]。如下:
>>>a = [] #空列表
>>>b = []
>>>id(a) #96384072
>>>id(b) #96384264
>>>a = [1] #短列表
>>>b = [1]
>>>id(a) #41856328
>>>id(b) #41877640
>>>a = list(range(10000)) #長(zhǎng)列表
>>>b = list(range(10000))
>>>id(a) #41880840
>>>id(b) #41880904
4)tuple,元組型
對(duì)于元組而言,每次都開(kāi)辟新的內(nèi)存,生成新的對(duì)象;但是對(duì)于空元組(),則共享內(nèi)存。如下:
>>>a = () #空元組
>>>b = ()
>>>id(a) #3342408
>>>id(b) #3342408
>>>a = (1,) #短元組
>>>b = (1,)
>>>id(a) #37476560
>>>id(b) #37702512
>>>a = tuple(range(10000)) #長(zhǎng)元組
>>>b = tuple(range(10000))
>>>id(a) #40619720
>>>id(b) #40699784
4)dict,字典型
對(duì)于字典而言,比較簡(jiǎn)單,每次都開(kāi)辟新的內(nèi)存,生成新的對(duì)象,包括空字典{}。如下:
>>>a = {} #空字典
>>>b = {}
>>>id(a) #34237752
>>>id(b) #34237824
>>>a = {'x':1} #短字典
>>>b = {'x':1}
>>>id(a) #5467448
>>>id(b) #5467520
>>>a = dict(enumerate(range(10000))) #長(zhǎng)字典
>>>b = dict(enumerate(range(10000)))
>>>id(a) #41778560
>>>id(b) #41778632
(二)變量B=變量A,變量賦值給變量的情況
對(duì)于變量A對(duì)變量B賦值的情況(變量A必然已經(jīng)指向某個(gè)內(nèi)存對(duì)象了),無(wú)論哪種數(shù)據(jù)類(lèi)型,無(wú)論長(zhǎng)短復(fù)雜簡(jiǎn)單,都只有一個(gè)結(jié)果,即兩個(gè)變量都指向相同的內(nèi)存單元。這里用int舉例, 其他情況不再贅述。
>>>a = 1 #簡(jiǎn)短整數(shù),-5~256
>>>b = a
>>>id(a) #1598778384
>>>id(b) #1598778384
>>>a = 300 #復(fù)雜整數(shù)
>>>b = a
>>>id(a) #33205968
>>>id(b) #33205968