詳解Python變量在內存中的存儲

這篇文章主要是對python中的數據進行認識,對於不少初學者來說,其實數據的認識是最重要的,也是最容易出錯的。本文結合數據與內存形態講解python中的數據,內容包括:python

  • 引用與對象
  • 可變數據類型與不可變數據類型
  • 引用傳遞與值傳遞
  • 深拷貝與淺拷貝

id函數:你能夠經過python的內置函數 id() 來查看對象的身份(identity),這個所謂的身份其實就是 對象 的內存地址app

1、引用與對象:引用與對象的關係: 
ide

 
  1. #建立兩個對象函數

  2. name1='wupeiqi'優化

  3. name2='alex'this

對象:當建立數據對象時,在內存中會保存對象的值,這個值就是對象本身;(字符串對象:」wupeiqi」) 
引用:對象保存在內存空間,外部想要使用對象的值,須要使用引用,就是‘name1’,’name2’。內存會保存對象的引用數量,當某個對象的引用數量爲0時,對象會被回收。3d

2、可變數據類型與不可變數據類型 
1,數據分類:code

  • 可變數據類型:列表list和字典dict
  • 不可變數據類型:整型int、浮點型float、字符串型string和元組tuple

這裏的可變不可變,是指內存中的那塊內容(value)是否能夠被改變。若是是不可變類型,在對對象自己操做的時候,必須在內存中新申請一塊區域(由於老區域不可變)。若是是可變類型,對對象操做的時候,不須要再在其餘地方申請內存,只須要在此對象後面連續申請(+/-)便可,也就是它的address會保持不變,但區域會變長或者變短。對象

(1)python中的不可變數據類型,不容許變量的值發生變化,若是改變了變量的值,至關因而新建了一個對象,而對於相同的值的對象,在內存中則只有一個對象,內部會有一個引用計數來記錄有多少個變量引用這個對象; 
(2)可變數據類型,容許變量的值發生變化,即若是對變量進行append、+=等這種操做後,只是改變了變量的值,而不會新建一個對象,變量引用的對象的地址也不會變化,不過對於相同的值的不一樣對象,在內存中則會存在不一樣的對象,即每一個對象都有本身的地址,至關於內存中對於同值的對象保存了多份,這裏不存在引用計數,是實實在在的對象。」blog

2,不可變數據類型:不可變是指對象自己的值是不可變的(當你建立a=1整型對象,用a去引用它,內存中的對象1是不變得,當執行a=2時,只是從新建立了對象2,用a引用,若是1對象沒有其餘引用會被回收)

 
  1. >>> x = 1

  2. >>> id(x)

  3. 31106520

  4. >>> y = 1

  5. >>> id(y)

  6. 31106520

  7. >>> x = 2

  8. >>> id(x)

  9. 31106508

  10. >>> y = 2

  11. >>> id(y)

  12. 31106508

  13. >>> z = y

  14. >>> id(z)

  15. 31106508

解釋:這裏的不可變你們能夠理解爲x引用的地址處的值是不能被改變的,也就是31106520地址處的值在沒被垃圾回收以前一直都是1,不能改變,若是要把x賦值爲2,那麼只能將x引用的地址從31106520變爲31106508,至關於x = 2這個賦值又建立了一個對象,即2這個對象,而後x、y、z都引用了這個對象,因此int這個數據類型是不可變的,若是想對int類型的變量再次賦值,在內存中至關於又建立了一個新的對象,而再也不是以前的對象。從下圖中就能夠看到上面程序的過程。 

3,可變對象:可變是指對象自己的值是可變的list,dict對象的值實際上是引用了其餘對象,當改變對象的值時,實際上是引用了不一樣的對象)

 
  1. >>> a = [1, 2, 3]

  2. >>> id(a)

  3. 41568816

  4. >>> a = [1, 2, 3]

  5. >>> id(a)

  6. 41575088

  7. >>> a.append(4)

  8. >>> id(a)

  9. 41575088

  10. >>> a += [2]

  11. >>> id(a)

  12. 41575088

  13. >>> a

  14. [1, 2, 3, 4, 2]

解釋:(1)進行兩次a = [1, 2, 3]操做,兩次a引用的地址值是不一樣的,也就是說其實建立了兩個不一樣的對象,這一點明顯不一樣於不可變數據類型,因此對於可變數據類型來講,具備一樣值的對象是不一樣的對象,即在內存中保存了多個一樣值的對象,地址值不一樣。 
(2)咱們對列表進行添加操做,分別a.append(4)和a += [2],發現這兩個操做使得a引用的對象值變成了上面的最終結果,可是a引用的地址依舊是41575088,也就是說對a進行的操做不會改變a引用的地址值,只是在地址後面又擴充了新的地址,改變了地址裏面存放的值,因此可變數據類型的意思就是說對一個變量進行操做時,其值是可變的,值的變化並不會引發新建對象,即地址是不會變的,只是地址中的內容變化了或者地址獲得了擴充。下圖對這一過程進行了圖示,能夠很清晰地看到這一過程。 

3、引用傳遞與值傳遞:可變對象爲引用傳遞,不可變對象爲值傳遞。(函數傳值) 
1,引用傳遞:當傳遞列表或者字典時,若是改變引用的值,就修改了原始的對象。

 
  1. # 添加了一個string類型的元素添加到末尾

  2.  
  3. def ChangeList(lis):

  4. lis.append('hello i am the addone')

  5. print lis

  6. return

  7.  
  8. lis = [1, 2, 3]

  9. ChangeList(lis)

  10. print lis

  11.  
  12. 輸出:

  13. [1,2,3, 'hello i am the addone']

  14.  
  15. [1,2, 3,'hello i am the addone']

2,值傳遞:當傳遞不可變對象時,若是改變引用的值,只是建立了不一樣的對象,原始對象並無改變。

 
  1. def ChangeString(string):

  2. string = 'i changed as this'

  3. print string

  4. return

  5.  
  6. string = 'hello world'

  7. ChangeString(string)

  8. print string

  9.  
  10. 輸出:

  11. i changed as this

  12.  
  13. hello world

4、深拷貝與淺拷貝: 
copy.copy() 淺拷貝;copy.deepcopy() 深拷貝。淺拷貝是新建立了一個跟原對象同樣的類型,可是其內容是對原對象元素的引用。這個拷貝的對象自己是新的,但內容不是拷貝序列類型對象(列表\元組)時,默認是淺拷貝。

1,賦值拷貝: 
賦值,只是建立一個變量,該變量指向原來內存地址:n4 = n3 = n2 = n1 = 「123/’Wu’」 

2,淺拷貝:在內存中只額外建立第一層數據

 
  1. import copy

  2. n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}

  3. n3 = copy.copy(n1)

  • 1
  • 2
  • 3

這裏寫圖片描述

 
  1. import copy

  2. a = [1,[[2,3],5],3]

  3. b = a.copy() #copy.copy(a)

  4.  
  5. print(id(a[1]))

  6. print(id(b[1]))

  7.  
  8. c = copy.deepcopy(a)

  9. print(id(c[1]))

  10.  
  11. 輸出:

  12. 3021497843400

  13. 3021497843400

  14. 3021497854728

3,深拷貝:在內存中將全部的數據從新建立一份(排除最後一層,即:python內部對字符串和數字的優化)

 
  1. import copy

  2. n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}

  3. n4 = copy.deepcopy(n1)

相關文章
相關標籤/搜索