Python變量的賦值、淺拷貝和深拷貝詳解
前言
python中為聲明一個變量有三種方法:賦值、淺拷貝(shadow copy)、深拷貝(deep copy),相信每個pythoner或多或少都知道他們之間的區(qū)別,但在某些點上,還是會踩坑,這篇文章記錄下所有關于這三者區(qū)別的疑問。
對比
在理解以上幾個概念區(qū)別之前需要明確以下問題:
- 賦值傳遞的是原始對象的地址
- 函數(shù)傳遞形參也是傳遞地址,該地址指向原始對象,多進程的task函數(shù)除外,多進程中task函數(shù)的形參和原始對象是完全不同的兩個對象,類似深拷貝(進程間內(nèi)存獨立)
- 淺拷貝,除了顯式地調(diào)用copy庫,還可以使用如切片、list()、dict()等函數(shù)達到淺拷貝的效果(下面會給例子)。
- 當我們討論python中的拷貝時,默認針對的是可變對象,因為對于不可變對象來講,拷貝是沒有意義的,python解釋器在啟動時就將不可變對象放在內(nèi)存池中了,它在內(nèi)存的位置在解釋器終止之前不會變。假設我們通過賦值、淺拷貝、深拷貝得到了一個新的對象,那么與原始對象相比,有以下結論:
/ | 賦值 | 淺拷貝 | 深拷貝 |
id是否變化 | × | ? | ? |
修改對象本身是否互相影響 | ? | × | × |
修改嵌套對象是否互相影響 | ? | ? | × |
具體例子
a = ['a', 1, [1,2]] b = a c = copy.copy(a) d = copy.deepcopy(a) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}') print(f'c is: {c} , id is: {id(c)}') print(f'd is: vvxyksv9kd , id is: {id(d)}')
輸出:
a is: ['a', 1, [1, 2]] , id is: 140211144620672
b is: ['a', 1, [1, 2]] , id is: 140211144620672
c is: ['a', 1, [1, 2]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992
可以看出,賦值得到的新對象id與原始對象id相同,拷貝得到的新對象id與原始對象不同。
# 對對象本身進行修改,即“最外層” a.append(2) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}') print(f'c is: {c} , id is: {id(c)}') print(f'd is: vvxyksv9kd , id is: {id(d)}')
輸出:
a is: ['a', 1, [1, 2], 2] , id is: 140211144620672
b is: ['a', 1, [1, 2], 2] , id is: 140211144620672
c is: ['a', 1, [1, 2]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992
可以看出,修改對象本身對拷貝對象沒有影響。 再修改嵌套對象:
# 修改嵌套對象 a[2].append(3) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}') print(f'c is: {c} , id is: {id(c)}') print(f'd is: vvxyksv9kd , id is: {id(d)}')
輸出:
a is: ['a', 1, [1, 2, 3], 2] , id is: 140211144620672
b is: ['a', 1, [1, 2, 3], 2] , id is: 140211144620672
c is: ['a', 1, [1, 2, 3]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992
可以看出,修改嵌套對象,對淺拷貝對象有影響,對深拷貝對象無影響。 再看下不可變對象1的id:
print(f'1 id in a is {id(a[1])}') print(f'1 id in b is {id(b[1])}') print(f'1 id in c is {id(c[1])}') print(f'1 id in d is {id(d[1])}')
輸出:
1 id in a is 140279957373168
1 id in b is 140279957373168
1 id in c is 140279957373168
1 id in d is 140279957373168
可以看出,所有對象的不可變對象指向了同一個位置,事實上,即便在同一個對象中相同的不可變對象也是指向同一個位置:
print(f'a is {a}') print(f'{id(a[1])}, {id(a[2][0])}')
輸出:
a is ['a', 1, [1, 2, 3], 2]
140168057503984, 140168057503984
除了顯示地使用copy.copy()獲得一個淺拷貝對象,list()、dict()等對象工廠也可以獲得一個淺拷貝對象:
a = ['a', 1, [1,2]] b = list(a) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}') a[2].append(3) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}')
輸出:
a is: ['a', 1, [1, 2]] , id is: 139717765197952
b is: ['a', 1, [1, 2]] , id is: 139718287865280
a is: ['a', 1, [1, 2, 3]] , id is: 139717765197952
b is: ['a', 1, [1, 2, 3]] , id is: 139718287865280
可以看出,a與b的id不同,但修改嵌套對象會互相影響,所以b是a的淺拷貝對象。
切片也類似,是一個淺拷貝對象:
a = ['a', 1, [1,2]] b = a[-1:] print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}') a[2].append(3) print(f'a is: {a} , id is: {id(a)}') print(f'b is: , id is: {id(b)}')
輸出:
a is: ['a', 1, [1, 2]] , id is: 140294636448256
b is: [[1, 2]] , id is: 140294636449408
a is: ['a', 1, [1, 2, 3]] , id is: 140294636448256
b is: [[1, 2, 3]] , id is: 140294636449408
到此這篇關于Python變量的賦值、淺拷貝和深拷貝詳解的文章就介紹到這了,更多相關Python賦值、淺拷貝和深拷貝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
pymysql之cur.fetchall() 和cur.fetchone()用法詳解
這篇文章主要介紹了pymysql之cur.fetchall() 和cur.fetchone()用法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05Python cookbook(數(shù)據(jù)結構與算法)從字典中提取子集的方法示例
這篇文章主要介紹了Python cookbook(數(shù)據(jù)結構與算法)從字典中提取子集的方法,涉及Python字典推導式的相關使用技巧,需要的朋友可以參考下2018-03-03python des,aes,rsa加解密的實現(xiàn)
這篇文章主要介紹了python des,aes,rsa加解密的實現(xiàn),幫助大家更好的理解和使用python,感興趣的朋友可以了解下2021-01-01Python如何實現(xiàn)自動生成指定文件名并自定義后綴名
這篇文章主要為大家詳細介紹了Python如何實現(xiàn)名稱為000- 999-文件的自動生成,并且后綴名可以自定義,感興趣的小伙伴可以跟隨小編一起學習一下2025-02-02