這篇文章主要是對python中的數據進行認識,對於很多初學者來講,其實數據的認識是最重要的,也是最容易出錯的。本文結合數據與內存形態講解python中的數據,內容包括:
- 引用與對象
- 可變數據類型與不可變數據類型
- 引用傳遞與值傳遞
- 深拷貝與淺拷貝
(id函數:你可以通過python的內置函數 id() 來查看對象的身份(identity),這個所謂的身份其實就是 對象 的內存地址)
一、引用與對象:引用與對象的關系:
-
#創建兩個對象
-
name1= 'wupeiqi'
-
name2= 'alex'
- 1
- 2
- 3
對象:當創建數據對象時,在內存中會保存對象的值,這個值就是對象自己;(字符串對象:”wupeiqi”)
引用:對象保存在內存空間,外部想要使用對象的值,需要使用引用,就是‘name1’,’name2’。內存會保存對象的引用數量,當某個對象的引用數量為0時,對象會被回收。
二、可變數據類型與不可變數據類型
1,數據分類:
- 可變數據類型:列表list和字典dict
- 不可變數據類型:整型int、浮點型float、字符串型string和元組tuple
這里的可變不可變,是指內存中的那塊內容(value)是否可以被改變。如果是不可變類型,在對對象本身操作的時候,必須在內存中新申請一塊區域(因為老區域不可變)。如果是可變類型,對對象操作的時候,不需要再在其他地方申請內存,只需要在此對象后面連續申請(+/-)即可,也就是它的address會保持不變,但區域會變長或者變短。
(1)python中的不可變數據類型,不允許變量的值發生變化,如果改變了變量的值,相當於是新建了一個對象,而對於相同的值的對象,在內存中則只有一個對象,內部會有一個引用計數來記錄有多少個變量引用這個對象;
(2)可變數據類型,允許變量的值發生變化,即如果對變量進行append、+=等這種操作后,只是改變了變量的值,而不會新建一個對象,變量引用的對象的地址也不會變化,不過對於相同的值的不同對象,在內存中則會存在不同的對象,即每個對象都有自己的地址,相當於內存中對於同值的對象保存了多份,這里不存在引用計數,是實實在在的對象。”
2,不可變數據類型:不可變是指對象本身的值是不可變的(當你創建a=1整型對象,用a去引用它,內存中的對象1是不變得,當執行a=2時,只是重新創建了對象2,用a引用,如果1對象沒有其他引用會被回收)
-
-
-
31106520
-
-
-
31106520
-
-
-
31106508
-
-
-
31106508
-
-
-
31106508
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
解釋:這里的不可變大家可以理解為x引用的地址處的值是不能被改變的,也就是31106520地址處的值在沒被垃圾回收之前一直都是1,不能改變,如果要把x賦值為2,那么只能將x引用的地址從31106520變為31106508,相當於x = 2這個賦值又創建了一個對象,即2這個對象,然后x、y、z都引用了這個對象,所以int這個數據類型是不可變的,如果想對int類型的變量再次賦值,在內存中相當於又創建了一個新的對象,而不再是之前的對象。從下圖中就可以看到上面程序的過程。
3,可變對象:可變是指對象本身的值是可變的(list,dict對象的值其實是引用了其他對象,當改變對象的值時,其實是引用了不同的對象)
-
-
-
41568816
-
-
-
41575088
-
-
-
41575088
-
-
-
41575088
-
-
[ 1, 2, 3, 4, 2]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
解釋:(1)進行兩次a = [1, 2, 3]操作,兩次a引用的地址值是不同的,也就是說其實創建了兩個不同的對象,這一點明顯不同於不可變數據類型,所以對於可變數據類型來說,具有同樣值的對象是不同的對象,即在內存中保存了多個同樣值的對象,地址值不同。
(2)我們對列表進行添加操作,分別a.append(4)和a += [2],發現這兩個操作使得a引用的對象值變成了上面的最終結果,但是a引用的地址依舊是41575088,也就是說對a進行的操作不會改變a引用的地址值,只是在地址后面又擴充了新的地址,改變了地址里面存放的值,所以可變數據類型的意思就是說對一個變量進行操作時,其值是可變的,值的變化並不會引起新建對象,即地址是不會變的,只是地址中的內容變化了或者地址得到了擴充。下圖對這一過程進行了圖示,可以很清晰地看到這一過程。
三、引用傳遞與值傳遞:可變對象為引用傳遞,不可變對象為值傳遞。(函數傳值)
1,引用傳遞:當傳遞列表或者字典時,如果改變引用的值,就修改了原始的對象。
-
# 添加了一個string類型的元素添加到末尾
-
-
def ChangeList(lis):
-
lis.append( 'hello i am the addone')
-
print lis
-
return
-
-
lis = [ 1, 2, 3]
-
ChangeList(lis)
-
print lis
-
-
輸出:
-
[ 1,2,3, 'hello i am the addone']
-
-
[ 1,2, 3,'hello i am the addone']
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
2,值傳遞:當傳遞不可變對象時,如果改變引用的值,只是創建了不同的對象,原始對象並沒有改變。
-
def ChangeString(string):
-
string = 'i changed as this'
-
print string
-
return
-
-
string = 'hello world'
-
ChangeString( string)
-
print string
-
-
輸出:
-
i changed as this
-
-
hello world
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
四、深拷貝與淺拷貝:
copy.copy() 淺拷貝;copy.deepcopy() 深拷貝。淺拷貝是新創建了一個跟原對象一樣的類型,但是其內容是對原對象元素的引用。這個拷貝的對象本身是新的,但內容不是。拷貝序列類型對象(列表\元組)時,默認是淺拷貝。
1,賦值拷貝:
賦值,只是創建一個變量,該變量指向原來內存地址:n4 = n3 = n2 = n1 = “123/’Wu’”
2,淺拷貝:在內存中只額外創建第一層數據
-
import copy
-
n1 = { "k1": "wu", "k2": 123, "k3": ["alex", 456]}
-
n3 = copy.copy(n1)
- 1
- 2
- 3
-
import copy
-
a = [ 1,[[2,3],5],3]
-
b = a. copy() #copy.copy(a)
-
-
print(id(a[1]))
-
print(id(b[1]))
-
-
c = copy.deepcopy(a)
-
print(id(c[1]))
-
-
輸出:
-
3021497843400
-
3021497843400
-
3021497854728
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3,深拷貝:在內存中將所有的數據重新創建一份(排除最后一層,即:python內部對字符串和數字的優化)
-
import copy
-
n1 = { "k1": "wu", "k2": 123, "k3": ["alex", 456]}
-
n4 = copy.deepcopy(n1)
- 1
- 2
- 3
參考文獻:
http://blog.csdn.net/dan15188387481/article/details/49864613
https://www.cnblogs.com/lfpython/p/7207747.html
https://www.cnblogs.com/huamingao/p/5809936.html
https://www.cnblogs.com/jiangzhaowei/p/5740913.html