Python中的變量
Python中的變量可以看作數值的一個標簽,當給變量賦值時,該變量將指向該值域的存儲空間,給該變量重新賦一個新值時,將釋放當前值域的存儲空間,指向另一個值域存儲空間。
對於Python來說是以值域為內存分配對象,變量名只是指向該內存空間,當值域發生變化時,系統分配另外一塊內存,該變量指向新分配的地址塊,原來地址空間系統進行回收,該方式也就說明了為何python的變量可以為任何類型,而C/C++則是先給變量分配一塊適當的大小,當值變化時,直接更新變量指向地址的內存塊,及變量的地址永遠不變,所以它就必須為固定的類型。
python中可以通過id(a)來查看變量a的地址,例如如下測試:
當存在如下情況時:
a = 100, b = 100 時,此時id(a) 和id(b)是相等的,但是當a = 7823.232, b = 7823.232時,此時id(a)和id(b)的地址有可能相同也有可能不同,這個取決於計算機系統(32位上測試不同,64位上相同)。python對[-5, 257]區間的整數和短字符串做了內存緩存,當不同的變量等於同一個值時,變量的地址都相同,其他的數會各自維護一個地址空間。
變量賦值
變量賦值為值域地址的引用,比如a = 0, b = a,a和b將指向同一塊地址,當b = 10的時候,b又指向了另外的一塊地址空間。
淺拷貝
對於可變數據類型,進行外部的空間拷貝,其內部都是引用相同的地址。
淺拷貝函數:list的[:],list(),set(), map(),及對應的copy函數,copy模塊中的copy函數。
要點:(1)可變數據類型。
(2)只拷貝最外部的存儲空間。
(3)對其內部可變元素得修改會體現在被拷貝得元素上。
深拷貝
對於可變數據類型或者其內部元素包含可變類型,可變類型數據將拷貝一份獨立的空間,不可變類型將引用相同的地址。
要點:(1)可變類型或者包含可變元素得不可變類型
(2)可變類型數據都將拷貝一份獨立得空間
(3)對其內部可變元素得修改不會體現在被拷貝得元素上
注意:不可變類型且內部元素不包含可變類型(如tuple包含list)的淺拷貝,深拷貝都是賦值操作。
賦值,淺拷貝,深拷貝關系如下圖
變量的引用計數
既然Python的變量都是地址的引用,那么如果 a = b后,把a釋放掉,b會一起釋放空間嗎?
我們發現,當del a后,b的地址並沒有發生變化,而且b的值也沒被釋放。
這是因為在賦值(地址引用)時,會有個計數器,當引用增加時,計數器對應增加,引用減少(釋放或者生命周期結束),計數器對應減少,當計數器減少到0的時候,啟動系統垃圾回收機制對存儲空間進行回收,當然Python的內存回收不會當計數器減少為0時馬上回收,如果一段時間內,有新的內存申請需要,則會復用現在的內存空間,否則就釋放。
我們可以使用 sys.getrefcount() 查看變量被引用的次數
我們發現,為啥計數從2開始呢,其實這個是因為調用函數getrefcount時,參數傳遞時,也進行了一次地址的引用,所以比實際的引用會多一次。