首先分享Python里面的數據類型
1、不可變類型:Number(數字)、String(字符串)、Tuple(元組)。
不可變數據類型在第一次聲明賦值的時候, 會在內存中開辟一塊空間, 用來存放這個變量被賦的值,? 而這個變量實際上存儲的, 并不是被賦予的這個值, 而是存放這個值所在空間的內存地址, 通過這個地址, 變量就可以在內存中取出數據了. 所謂不可變就是說, 我們不能改變這個數據在內存中的值, 所以當我們改變這個變量的賦值時, 只是在內存中重新開辟了一塊空間, 將這一條新的數據存放在這一個新的內存地址里, 而原來的那個變量就不在引用原數據的內存地址而轉為引用新數據的內存地址了.
如圖:
In [11]: a = 1 #int為不可變類型,賦值
?
In [12]: id(a) #查看a地址
Out[12]: 4541615920In [13]: id(1) #查看數字1的地址
Out[13]: 4541615920?
In [14]: a = 2 #為a重新賦值
?
In [15]: id(a) #查看a地址,指向新的內存地址
Out[15]: 4541615952?
In [16]: id(2)
Out[16]: 4541615952?
In [17]: id(1)
Out[17]: 4541615920
In [28]: a = "hello" #為a賦值 hello
?
In [29]: b = a #將a賦值給a
?
In [30]: id(a)
Out[30]: 4572905712?
In [31]: id(b) #通過查看a和b的地址,說明a和b指向同一個地址
Out[31]: 4572905712?
In [32]: b = "hi" #為b賦值 hi
?
In [33]: a #查看a未被修改
Out[33]: 'hello'?
In [34]: id(a) #查看a地址未發生修改
Out[34]: 4572905712?
In [35]: id(b) #查看b指向新的地址,說明b=a時,是將a的引用指向b
Out[35]: 4544178736
b=a直接賦值時,默認是淺拷貝傳遞對象的引用而已,原始數據若為不可變類型時,被賦值的b改變時,相當于在內存中重新開辟了一塊空間, 將這一條新的數據存放在這一個新的內存地址里, 而原來的那個變量就不在引用原數據的內存地址而轉為引用新數據的內存地址了.
2、可變類型:Set(集合)、List(列表)、Dictionary(字典)。
當數據類型對應的變量的值發生改變時,它對應的內存地址也會發生改變,即當?e?=?[1,2]?時,是e指向列表[1,2]對象,而不是將e指向列表[1,2]的地址
如圖:
In [10]: e = [1,2]
?
In [11]: id(e)
Out[11]: 4408548080?
In [12]: id([1,2]) #e指向[1,2]對象,而不是[1,2]的地址
Out[12]: 4408714656
In [10]: e = [1,2] #給變量e賦值可變類型列表[1,2]
?
In [11]: id(e) #查看e地址
Out[11]: 4408548080?
In [12]: id([1,2]) #查看列表[1,2]地址
Out[12]: 4408714656?
In [13]: f = e #將e賦值給f,兩者共享同一個列表[1,2]對象
?
In [14]: id(f) #查看f地址,其實是將c指向于e的引用地址,而非列表[1,2]的實際地址
Out[14]: 4408548080?
In [15]: e[0] = 11 #通過e修改列表內元素
?
In [16]: e
Out[16]: [11, 2]
?
In [17]: f #查看f中對應元素同樣被修改,驗證e和f指向一個引用對象,說明f=e時,是將e的對象的地址指向f
Out[17]: [11, 2]
f?=?e直接賦值時,默認是淺拷貝傳遞對象的引用而已,原始數據e若為可變類型發生變化,被賦值的f也會做相同的改變。
3. 淺拷貝
淺拷貝是對于一個對象的頂層拷貝
通俗的理解是:拷貝了引用,并沒有拷貝內容
In [36]: a = [1,2]
?
In [37]: b =a
?
In [38]: id(a)
Out[38]: 4573291056?
In [39]: id(b) #淺拷貝,a和b地址相同,說明b=a是對a引用的拷貝
Out[39]: 4573291056
通過上面的結果,說明當給一個變量賦值時。其實就是講數據的引用復制了一份給另外一個變量 ,就是屬于簡單的淺拷貝
In [36]: a = [1,2]
?
In [37]: b =a
?
In [38]: id(a)
Out[38]: 4573291056?
In [39]: id(b)
Out[39]: 4573291056?
In [40]: a[0] = 11?
In [41]: a #a中元素被修改
Out[41]: [11, 2]
?
In [42]: b #b中元素被修改
Out[42]: [11, 2]
通過上面的結果,說明通過任何一個引用修改可變變量之后,另外一個變量的值也發生了變化。
In [24]: g = [2,3]
?
In [25]: h = copy.copy(g) #淺拷貝
?
In [26]: id(g)
Out[26]: 4409269536?
In [27]: id(h)
Out[27]: 4408716176?
In [28]: id([2,3])
Out[28]: 4408685824?
In [30]: id(2) #獲取2的地址
Out[30]: 4378861392?
In [31]: id(g[0]) #獲取g[0]位置的地址
Out[31]: 4378861392?
In [32]: id(h[0]) #獲取h[0]位置的地址
Out[32]: 4378861392
通過上面的結果,說明淺拷貝只是對外層引用的拷貝,內層地址一樣。
In [33]: a = [11,22]
?
In [34]: b = [33,44]
?
In [35]: c =[a,b]
?
In [36]: id(a)
Out[36]: 4408713536?
In [37]: id(b)
Out[37]: 4408547680?
In [38]: id(c)
Out[38]: 4408615712?
In [39]: d = copy.copy(c) #對c進行淺拷貝,得到d
?
In [40]: id(d) #查看d地址
Out[40]: 4409421248?
In [41]: id(d[0]) #查看d[0]位置元素地址,與a地址相同,說明只進行外層拷貝
Out[41]: 4408713536?
In [42]: id(d[1]) #查看d[1]位置元素地址,與b地址相同,說明只進行外層拷貝
Out[42]: 4408547680?
In [43]: a.append(55) #給a列表添加元素
?
In [44]: c #查看c中元素也發生變化
Out[44]: [[11, 22, 55], [33, 44]]
?
In [45]: d #查看d中元素也發生變化,說明是對外層的拷貝
Out[45]: [[11, 22, 55], [33, 44]]
?
In [46]: d.append(66) #給d中添加元素
?
In [47]: d #查看d中元素發生變化
Out[47]: [[11, 22, 55], [33, 44], 66]
?
In [48]: c #查看c中元素未發生變化,說明d未外層拷貝
Out[48]: [[11, 22, 55], [33, 44]]
通過上面的結果,說明淺拷貝只是對外層引用的拷貝,內層地址一樣,修改外層,拷貝的新對象不會發生變化,修改內層元素,拷貝的新對象會發生變化。
4. 深拷貝
深拷貝是對于一個對象所有層次的拷貝(遞歸), 所以原始對象的改變不會造成深拷貝里任何子元素的改變
In [61]: a = [11,22]
?
In [62]: b = [33,44]
?
In [63]: c =[a,b]
?
In [64]: c
Out[64]: [[11, 22], [33, 44]]
?
In [65]: d = copy.deepcopy(c) #深拷貝得到d
?
In [66]: d
Out[66]: [[11, 22], [33, 44]]
?
In [67]: f = copy.copy(c) #淺拷貝得到f
?
In [68]: f
Out[68]: [[11, 22], [33, 44]]
?
In [69]: a.append(55) #a中添加元素55
?
In [70]: d #深拷貝的d中沒有修改,說明是對內層也進行copy
Out[70]: [[11, 22], [33, 44]]
?
In [71]: f #淺拷貝的f中發生變化,說明是只對外層進行了copy
Out[71]: [[11, 22, 55], [33, 44]]