網(wǎng)站首頁 編程語言 正文
Python中append淺拷貝機制
關于深淺拷貝,最直觀的理解就是:
- 深拷貝:拷貝的程度深,自己新開辟了一塊內(nèi)存,將被拷貝內(nèi)容全部拷貝過來了;
- 淺拷貝:拷貝的程度淺,只拷貝原數(shù)據(jù)的首地址,然后通過原數(shù)據(jù)的首地址,去獲取內(nèi)容。
這兩者的優(yōu)缺點對比:
- 深拷貝拷貝程度高,將原數(shù)據(jù)復制到新的內(nèi)存空間中。改變拷貝后的內(nèi)容不影響原數(shù)據(jù)內(nèi)容。但是深拷貝耗時長,且占用內(nèi)存空間。
- 淺拷貝拷貝程度低,只復制原數(shù)據(jù)的地址。其實是將副本的地址指向原數(shù)據(jù)地址。修改副本內(nèi)容,是通過當前地址指向原數(shù)據(jù)地址,去修改。所以修改副本內(nèi)容會影響到原數(shù)據(jù)內(nèi)容。但是淺拷貝耗時短,占用內(nèi)存空間少。
Python內(nèi)存引用
在C語言中,在聲明變量的時候,int a,int b,這兩條語句為a,b兩個變量分別賦予了兩塊不同的內(nèi)存空間,然后賦值的時候再將相應的值存儲到對應的存儲空間。但是在Python中變量的賦值與C語言是截然不同的,考慮下面的代碼:
>>> a = 2
>>> b = 2
>>> id(a)
140736259334576
>>> id(b)
140736259334576
id函數(shù)用于獲取對象的內(nèi)存地址,可以發(fā)現(xiàn),變量a和變量b的內(nèi)存地址竟然一樣!
在Python中,先生成對象,變量再對對象進行引用,在這個例子中,1就是對象,然后a再對1進行引用,由于常數(shù)是不可變類型,所以1的內(nèi)存空間是一樣的,所以a,b引用的是用一塊內(nèi)存空間。雖然變量名不一樣,但是他們引用的對象是相同的。
當然上面舉的例子是int類型的,這屬于不可變類型。如果是list或者dict呢?來看看下面的例子:
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
3145735383560
>>> id(b)
3145735414984
內(nèi)存地址不一致!基于此,我們步入今天的主題,來看看append方法淺拷貝機制,到底有什么坑!
append方法淺拷貝機制
Python中的append方法是一個常用的方法,可以將一個對象添加到列表末尾。相信大家一定都用過吧?有人去深挖這個函數(shù)的用法嗎?這里面可以存在一個大坑!
我們來看一個例子:
>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(a)
>>> b
[[1, 3, 5, 'a']]
>>> a.append("aha")
>>> b # surprise?
[[1, 3, 5, 'a', 'aha']]
思考一下,明明在第三行之后并沒有對b操作,那么為什么b會發(fā)生改變呢?
回到今天的主題,事實上,append方法是淺拷貝。在Python中,對象賦值實際上是對象的引用,當創(chuàng)建一個對象,然后把它賦值給另一個變量的時候,Python并沒有拷貝這個對象,而只是拷貝了這個對象的引用,這就是淺拷貝。
我們逐步來看。首先,b.append(a)就是對a進行了淺拷貝,結果為b=[[1, 3, 5, 'a']],但b[0]與a引用的對象是相同的,這可以通過id函數(shù)進行驗證:
>>> id(b[0])
3145735177480
>>> id(a)
3145735177480
可見,b[0]與a指向同個內(nèi)存地址。
下一步,代碼執(zhí)行a.append(0),列表是可變類型,這一步在原地址的列表的末尾添加0,原地址的內(nèi)容被改變了但是地址沒有變(可以將Python中的list理解為鏈表,所以這個list的地址不會變,這相當于鏈表的頭結點),所以a和b[0]的內(nèi)容同時被改變了,這就是為什么對a進行append操作b會跟著發(fā)生改變。
發(fā)生這些的前提是對同一個地址上的內(nèi)容進行操作,所以影響了指向該地址的所有變量。
所以,在日常使用append函數(shù)的時候,就需要將淺拷貝變?yōu)樯羁截悾袃蓚€解決方案:
- b.append(list(a))
- b.append(a[:])
還是上面的例子,來看看這兩個方法的結果是不是真的解決了append淺拷貝問題。
>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(list(a))
>>> b
[[1, 3, 5, 'a']]
>>> a.append(0)
>>> a
[1, 3, 5, 'a', 0]
>>> b
[[1, 3, 5, 'a']]
>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(a[:])
>>> b
[[1, 3, 5, 'a']]
>>> a.append(10)
>>> a
[1, 3, 5, 'a', 10]
>>> b
[[1, 3, 5, 'a']]
怎么樣,問題是不是解決了!所以日常使用中,一定要避免淺拷貝帶來的問題!
這個append的坑,也是我在刷leetcode:77. 組合時注意到的,題解為:
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
def traversal(n, k, start_index):
if len(path) == k:
result.append(path[:]) # 精華在這,要解決這里的淺拷貝問題!
return
for i in range(start_index, n + 1):
path.append(i)
traversal(n, k, i + 1)
path.pop()
path = []
result = []
traversal(n, k, 1)
return result
如果不處理第5行處的淺拷貝問題,會導致運行處下面的結果:
為啥?因為回溯呀,在上面代碼的第11行處,一直在向上回溯,所以結果運行出來就變成了空列表!
所以,在刷回溯的題的時候,如果你使用的是Python,一定要注意這一點了!
補充:Python append() 與深拷貝、淺拷貝
深淺拷貝
在 Python 中,對象賦值實際上是對象的引用。當創(chuàng)建一個對象,然后把它賦給另一個變量的時候,Python 并沒有拷貝這個對象,而只是拷貝了這個對象的引用,我們稱之為淺拷貝。
在 Python 中,為了使當進行賦值操作時,兩個變量互補影響,可以使用 copy 模塊中的 deepcopy 方法,稱之為深拷貝。
append() 函數(shù)
當 list 類型的對象進行 append 操作時,實際上追加的是該對象的引用。
id()?函數(shù):返回對象的唯一標識,可以類比成該對象在內(nèi)存中的地址。
>>>alist = []
>>> num = [2]
>>> alist.append( num )
>>> id( num ) == id( alist[0] )
True
如上例所示,當 num 發(fā)生變化時(前提是 id(num) 不發(fā)生變化),alist 的內(nèi)容隨之會發(fā)生變化。往往會帶來意想不到的后果,想避免這種情況,可以采用深拷貝解決:
alist.append( copy.deepcopy( num ) )
原文鏈接:https://blog.csdn.net/weixin_41951954/article/details/128837837
相關推薦
- 2022-03-31 解決Android加殼過程中mprotect調(diào)用失敗的原因分析_Android
- 2022-05-05 Android開發(fā)使用WebView打造web?app示例代碼_Android
- 2022-06-12 Python利用Matplotlib庫實現(xiàn)繪制餅形圖_python
- 2023-03-23 Android?View的事件分發(fā)機制深入分析講解_Android
- 2022-10-28 關于react中的常見錯誤及解決_React
- 2024-03-24 golang log包自定義輸出日志格式與寫入到文件
- 2023-03-27 拓撲排序Python實現(xiàn)的過程_python
- 2022-12-15 C++?Boost?MPI接口詳細講解_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支