那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Python如何防止數據被修改Python中的深拷貝與淺拷貝的問題說明

馬哥Linux運維 ? 來源:未知 ? 2019-03-30 09:54 ? 次閱讀

在平時工作中,經常涉及到數據的傳遞。在數據傳遞使用過程中,可能會發生數據被修改的問題。為了防止數據被修改,就需要再傳遞一個副本,即使副本被修改,也不會影響原數據的使用。為了生成這個副本,就產生了拷貝——今天就說一下Python中的深拷貝與淺拷貝的問題。

概念解讀

數據拷貝會涉及到Python中對象、可變類型、引用這3個概念,先來看看這幾個概念,只有明白了它們才能更好地理解拷貝到底是怎么一回事。

Python對象

在Python中,對對象有一種很通俗的說法,萬物皆對象。說的就是構造的任何數據類型都是一個對象,無論是數字、字符串、還是函數,甚至是模塊、Python都對當做對象處理。

所有Python對象都擁有三個屬性:身份、類型、值。

看一個簡單的例子:

In [1]: name ="laowang"# name對象In [2]: id(name) # id:身份的唯一標識Out[2]: 1698668550104In [3]:type(name)# type:對象的類型,決定了該對象可以保存什么類型的值Out[3]: strIn [4]: name # 對象的值,表示的數據Out[4]:'laowang'

可變與不可變對象

在Python中,按更新對象的方式,可以將對象分為2大類:可變對象與不可變對象。

可變對象: 列表、字典、集合。所謂可變是指可變對象的值可變,身份是不變的。

不可變對象:數字、字符串、元組。不可變對象就是對象的身份和值都不可變。新創建的對象被關聯到原來的變量名,舊對象被丟棄,垃圾回收器會在適當的時機回收這些對象。

In [7]: var1 ="python"In [8]:id(var1)Out[8]:1700782038408#由于var1是不可變的,重新創建了java對象,隨之id改變,舊對象python會在某個時刻被回收In [9]: var1 ="java"In [10]:id(var1)Out[10]:1700767578296

引用

在Python程序中,每個對象都會在內存中申請開辟一塊空間來保存該對象,該對象在內存中所在位置的地址被稱為引用。在開發程序時,所定義的變量名實際就對象的地址引用。

引用實際就是內存中的一個數字地址編號,在使用對象時,只要知道這個對象的地址,就可以操作這個對象,但是因為這個數字地址不方便在開發時使用和記憶,所以使用變量名的形式來代替對象的數字地址。在Python中,變量就是地址的一種表示形式,并不開辟開辟存儲空間。

就像 IP 地址,在訪問網站時,實際都是通過 IP 地址來確定主機,而 IP 地址不方便記憶,所以使用域名來代替 IP 地址,在使用域名訪問網站時,域名被解析成 IP 地址來使用。

通過一個例子來說明變量和變量指向的引用就是一個東西:

In [11]: age =18In [12]:id(age)Out[12]:1730306752In [13]:id(18)Out[13]:1730306752

逐步深入:引用賦值

上邊已經明白,引用就是對象在內存中的數字地址編號,變量就是方便對引用的表示而出現的,變量指向的就是此引用。賦值的本質就是讓多個變量同時引用同一個對象的地址。

那么在對數據修改時會發生什么問題呢?

不可變對象的引用賦值

對不可變對象賦值,實際就是在內存中開辟一片空間指向新的對象,原不可變對象不會被修改。原理圖如下:

下面通過案例來理解一下:

a與b在內存中都是指向1的引用,所以a、b的引用是相同的。

In [1]: a =1In [2]: b = aIn [3]:id(a)Out[3]:1730306496In [4]:id(b)Out[4]:1730306496

現在再給a重新賦值,看看會發生什么變化?從下面不難看出:當給a賦新的對象時,將指向現在的引用,不在指向舊的對象引用。

In [1]: a =1In [2]: b = aIn [5]: a =2In [6]:id(a)Out[6]:1730306816In [7]:id(b)Out[7]:1730306496

可變對象的引用賦值

可變對象保存的并不是真正的對象數據,而是對象的引用。當對可變對象進行賦值時,只是將可變對象中保存的引用指向了新的對象。原理圖如下:

仍然通過一個實例來體會一下,可變對象引用賦值的過程:當改變l1時,整個列表的引用會指新的對象,但是l1與l2都是指向保存的同一個列表的引用,所以引用地址不會變。

In [3]: l1 = [1,2,3]In [4]: l2 = l1In [5]:id(l1)Out[5]:1916633584008In [6]:id(l2)Out[6]:1916633584008In [7]: l1[0] =11In [8]:id(l1)Out[8]:1916633584008In [9]:id(l2)Out[9]:1916633584008

主旨詳解:淺拷貝、深拷貝

經過前2部分的解讀,大家對對象的引用賦值應該有了一個清晰的認識了。那么Python中如何解決原始數據在函數傳遞之后不受影響?這個問題Python已經幫我們解決了,使用對象的拷貝或者深拷貝就可以愉快解決了。

下面具體來看看Python中的淺拷貝與深拷貝是如何實現的。

淺拷貝

為了解決函數傳遞后被修改的問題,就需要拷貝一份副本,將副本傳遞給函數使用,就算是副本被修改,也不會影響原始數據 。

不可變對象的拷貝

不可變對象只在修改的時候才會在內存中開辟新的空間,而拷貝實際上是讓多個對象同時指向一個引用,和對象的賦值沒區別。

同樣的,通過一個實例來感受一下:不難看出,a與b指向相同的引用,不可變對象的拷貝就是對象賦值。

In [11]:importcopyIn [12]: a =10In [13]: b =copy.copy(a)In [14]: id(a)Out[14]:1730306496In [15]: id(b)Out[15]:1730306496

可變對象的拷貝

對于不可變對象的拷貝,對象的引用并沒有發生變化,那么可變對象的拷貝會不會和不可變對象一樣了?我們接著往下看。

通過下面的實例能看出:可變對象的拷貝會在內存中開辟一個新的空間來保存拷貝的數據。當再改變之前的對象時,對拷貝之后的對象沒有任何影響。

In [24]: importcopyIn [25]: l1 = [1,2,3]In [26]: l2 =copy.copy(l1)In [27]:id(l1)Out[27]:1916631742088In [28]:id(l2)Out[28]:1916636282952In [29]: l1[0] =11In [30]:id(l1)Out[30]:1916631742088In [31]:id(l2)Out[31]:1916636282952

原理圖如下:

現在再回到剛才那個問題,是不是淺拷貝就可以解決原始數據在函數傳遞之后不變的問題了?下面看一個稍微復雜一點的數據結構。

通過下面這個實例可以發現:復雜對象在拷貝時,并沒有解決數據在傳遞之后,數據改變的問題。出現這種原因,是copy() 函數在拷貝對象時只是將指定對象中的所有引用拷貝了一份,如果這些引用當中包含了一個可變對象的話,那么數據還是會被改變。這種拷貝方式,稱為淺拷貝。

In [35]: a = [1,2]In [36]: l1 = [3,4, a]In [37]: l2 =copy.copy(l1)In [38]:id(l1)Out[38]:1916631704520In [39]:id(l2)Out[39]:1916631713736In [40]: a[0] =11In [41]:id(l1)Out[41]:1916631704520In [42]:id(l2)Out[42]:1916631713736In [43]: l1Out[43]: [3,4, [11,2]]In [44]: l2Out[44]: [3,4, [11,2]]

原理圖如下:

對于上邊這種狀況,Python還提供了另一種拷貝方式(深拷貝)來解決。

深拷貝

區別于淺拷貝只拷貝頂層引用,深拷貝會逐層進行拷貝,直到拷貝的所有引用都是不可變引用為止。

接下來我們看看,要是將上邊的拷貝實例用使用深拷貝的話,原始數據改變的問題還會不會存在了?

下面的實例清楚地告訴我們:之前的問題就可以完美解決了。

importcopyl1 = [3,4, a]In [47]: l2 =copy.deepcopy(li)In [48]:id(l1)Out[48]:1916632194312In [49]:id(l2)Out[49]:1916634281416In [50]: a[0] =11In [51]:id(l1)Out[51]:1916632194312In [52]:id(l2)Out[52]:1916634281416In [54]: l1Out[54]: [3,4, [11,2]]In [55]: l2Out[55]: [[1, 2], 3, 4]

原理圖如下:

查漏補缺

為什么Python默認的拷貝方式是淺拷貝?

時間角度:淺拷貝花費時間更少;

空間角度:淺拷貝花費內存更少;

效率角度:淺拷貝只拷貝頂層數據,一般情況下比深拷貝效率高。

本文知識點總結:

不可變對象在賦值時會開辟新空間;

可變對象在賦值時,修改一個的值,另一個也會發生改變;

深、淺拷貝對不可變對象拷貝時,不開辟新空間,相當于賦值操作;

淺拷貝在拷貝時,只拷貝第一層中的引用,如果元素是可變對象,并且被修改,那么拷貝的對象也會發生變化;

深拷貝在拷貝時會逐層進行拷貝,直到所有的引用都是不可變對象為止;

Python中有多種方式實現淺拷貝,copy模塊的copy函數、對象的copy函數、工廠方法、切片等;

大多數情況下,編寫程序時都是使用淺拷貝,除非有特定的需求;

淺拷貝的優點:拷貝速度快,占用空間少,拷貝效率高。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內存
    +關注

    關注

    8

    文章

    3055

    瀏覽量

    74331
  • python
    +關注

    關注

    56

    文章

    4807

    瀏覽量

    85040

原文標題:Python 程序員如何防止數據被修改?

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Python面試必看的10個問題

    吧。 1、Python里面如何拷貝一個對象?(賦值,拷貝拷貝的區別)答:賦值(=),就是創
    發表于 02-28 17:00

    拷貝拷貝的實現方法概述

    拷貝拷貝的實現
    發表于 07-19 13:35

    python深淺拷貝是什么?

    python的直接賦值python拷貝python
    發表于 11-04 08:33

    請問哪位大神可以詳細介紹JavaScript拷貝拷貝

    JavaScript數據類型JavaScript拷貝拷貝
    發表于 11-05 07:16

    功能函數數據拷貝

    第16章 DSP功能函數-數據拷貝數據填充和浮點轉定點本期教程主要講解功能函數數據拷貝
    發表于 08-17 07:41

    C#拷貝拷貝區別解析

     所謂拷貝就是將對象的所有字段復制到新的副本對象拷貝對于值類型與引用類型的方式有區別,
    發表于 11-29 08:32 ?2.6w次閱讀
    C#<b class='flag-5'>淺</b><b class='flag-5'>拷貝</b>與<b class='flag-5'>深</b><b class='flag-5'>拷貝</b>區別解析

    實例介紹Python深淺拷貝

    【導語】:在工作,常涉及到數據的傳遞,在數據傳遞使用過程,可能會發生數據
    的頭像 發表于 12-16 11:34 ?1324次閱讀

    C++之拷貝構造函數的copy及copy

    C++編譯器會默認提供構造函數;無參構造函數用于定義對象的默認初始化狀態;拷貝構造函數在創建對象時拷貝對象的狀態;對象的拷貝拷貝
    的頭像 發表于 12-24 15:31 ?800次閱讀

    簡述Python深淺拷貝(copy)

    在工作,常涉及到數據的傳遞,在數據傳遞使用過程,可能會發生數據
    的頭像 發表于 07-29 16:55 ?1608次閱讀
    簡述<b class='flag-5'>Python</b><b class='flag-5'>中</b>深淺<b class='flag-5'>拷貝</b>(copy)

    C++面向對象編程拷貝拷貝

    可能對于Java程序員來說,很少遇到深淺拷貝問題,但是對于C++程序員來說可謂是又愛又恨。。
    的頭像 發表于 03-30 12:53 ?830次閱讀
    C++面向對象編程<b class='flag-5'>中</b>的<b class='flag-5'>深</b><b class='flag-5'>拷貝</b>和<b class='flag-5'>淺</b><b class='flag-5'>拷貝</b>

    C++拷貝拷貝詳解

    當類的函數成員存在指針成員時會產生拷貝拷貝和問題。
    發表于 08-21 15:05 ?360次閱讀
    C++<b class='flag-5'>深</b><b class='flag-5'>拷貝</b>和<b class='flag-5'>淺</b><b class='flag-5'>拷貝</b>詳解

    Python拷貝拷貝的操作

    【例子】拷貝拷貝 list1 = [ 123 , 456 , 789 , 213 ]list2 = list1list3 = lis
    的頭像 發表于 11-02 10:58 ?441次閱讀

    pythontuple的用法

    Python的元組(tuple)是一種不可變的有序集合。與列表(list)類似,元組可以存儲任意類型的數據,但是元組一旦創建就不能修改
    的頭像 發表于 11-21 16:27 ?1065次閱讀

    什么是零拷貝技術

    在傳統操作系統的數據傳輸過程中,系統內部會在磁盤、內存、緩存多次進行數據拷貝,每次都會占用CPU的資源,數據量小的時候還好。 隨著
    的頭像 發表于 11-27 16:20 ?494次閱讀
    什么是零<b class='flag-5'>拷貝</b>技術

    磁盤拷貝機會拷貝刪除的內容嗎

    磁盤拷貝機,也稱為硬盤克隆器或磁盤復制器,是一種用于復制硬盤驅動器內容的設備。它可以將一個硬盤上的所有數據,包括操作系統、程序、文件和設置,復制到另一個硬盤上。這種設備在數據備份、系統遷移、硬盤
    的頭像 發表于 10-14 15:38 ?598次閱讀
    澳门百家乐心| 玩百家乐怎么才能赢| 旅百家乐官网赢钱律| 百家乐连线游戏下载| 真人百家乐视频| 全讯网3344666| 德州扑克学校| 投注平台出租| 百家乐官网天天乐娱乐场| 百家乐官网赌博合作| 百家乐官网画面方法| 百家乐娱乐城怎么样| 百家乐电脑游戏机投注法实例| 黄金城百家乐免费下载| 新葡京娱乐城官方网站| 百家乐官网斗视频游戏| 百家乐官网的路子怎么| 百家乐可以算牌么| 大佬百家乐娱乐城| 百乐坊娱乐城官网| 百家乐官网投注限额| 百家乐星级游戏| 百家乐过滤工具| 林周县| 迪威百家乐官网娱乐网| 网上百家乐官网内幕| 百家乐下注技术| 大发888真人网站| 百家乐官网娱乐注册就送| 百家乐官网代理合作| 百家乐种类| 百家乐官网全讯网2| 澳博国际| 娱百家乐官网下载| 百家乐分析网| 优博国际娱乐| 娱网棋牌大厅| 安桌百家乐官网游戏百家乐官网 | 老虎机派通娱乐| 网上百家乐官网真的假的| 真钱百家乐哪里最好|