Python資料結構之----資料存放區與深淺拷貝

來源:互聯網
上載者:User

標籤:tps   nbsp   進階語言   情況   bsp   hello   字典   lis   等於   

  要深入的瞭解python的資料結構,就需要先瞭解一些Python中資料存放區的知識,進而理解Python中深淺拷貝的原理,接下來我們進一步的來學習。

一、Python的資料存放區

  在進階語言(C、C++、Java、Python)中,變數是對記憶體及其地址的抽象。在Python裡,一切變數都是對象,變數的儲存採用了引用的方式,儲存的只是一個變數值所在的記憶體位址,而不是變數的本身。即變數儲存的是對應資料的地址,我們將這種方式稱之為對象的引用。而採取這種儲存方式,變數儲存的只是一個引用,所以變數所需的儲存空間大小一致(類似於C語言的指標類型)。

  由於python中的變數都是採用的引用語義,資料結構可以包含基礎資料類型,導致了在python中每個變數中都儲存了這個變數的地址,而不是值本身;對於複雜的資料結構來說,裡面的儲存的也只只是每個元素的地址而已,下面給出基礎類型和資料結構類型變數重新賦值的儲存變化。我們來看一張簡單易懂的圖理解一下python的引用語義和C語言值語義在記憶體中的儲存情況,左右兩個圖,分別表示了python中變數儲存與C語言中變數儲存區別(參考:http://www.cnblogs.com/Eva-J/p/5534037.html):

  

1.資料類型重新初始化對python語義引用的影響

  變數的每一次初始化,都開闢了一個新的空間,將新內容的地址賦值給變數。對於來說,我們重複的給str1賦值,其實在記憶體中的變化如下右圖:

           

  從我們可以看出,str1在重複的初始化過程中,是因為str1中儲存的元素地址由‘hello world‘的地址變成了‘new hello world‘的。

 2.資料結構內部元素變化重對python語義引用的影響

  對於複雜的資料類型來說,改變其內部的值對於變數的影響:

  

  當對列表中的元素進行一些增刪改的操作的時候,是不會影響到lst1列表本身對於整個列表地址的,只會改變其內部元素的地址引用。可是當我們對於一個列表重新初始化(賦值)的時候,就給lst1這個變數重新賦予了一個地址,覆蓋了原本列表的地址,這個時候,lst1列表的記憶體id就發生了改變。上面這個道理用在所有複雜的資料類型中都是一樣的。

二、深淺拷貝

  1、變數的賦值

  搞明白了上面的內容,再來探討變數的賦值,就變得非常簡單了。

  2.str的賦值

      

   我們剛剛已經知道,str1的再次初始化(賦值)會導致記憶體位址的改變,從的結果我們可以看出修改了str1之後,被賦值的str2從記憶體位址到值都沒有受到影響。看記憶體中的變化,起始的賦值操作讓str1和str2變數都儲存了‘hello world’所在的地址,重新對str1初始化,使str1中儲存的地址發生了改變,指向了建立的值,此時str2變數儲存的記憶體位址並未改變,所以不受影響。

  3 .複雜的資料結構中的賦值

  剛剛我們看了單一資料型別的賦值,現在來看複雜資料結構變化對應記憶體的影響。

           

  對列表的增加修改操作,沒有改變列表的記憶體位址,lst1和lst2都發生了變化。對照記憶體配置圖我們不難看出,在列表中添加新值時,列表中又多儲存了一個新元素的地址,而列表本身的地址沒有變化,所以lst1和lst2的id均沒有改變並且都被添加了一個新的元素。簡單的比喻一下,我們出去吃飯,lst1和lst2就像是同桌吃飯的兩個人,兩個人公用一張桌子,只要桌子不變,桌子上的菜發生了變化兩個人是共同感受的。

  4.初識拷貝

  我們已經詳細瞭解了變數賦值的過程。對於複雜的資料結構來說,賦值就等於完全共用了資源,一個值的改變會完全被另一個值共用。然而有的時候,我們偏偏需要將一份資料的原始內容保留一份,再去處理資料,這個時候使用賦值就不夠明智了。python為這種需求提供了copy模組。提供了兩種主要的copy方法,一種是普通的copy,另一種是deepcopy。我們稱前者是淺拷貝,後者為深拷貝。

  深淺拷貝一直是所有程式設計語言的重要知識點,下面我們就從記憶體的角度來分析一下兩者的區別。

  5.淺拷貝

  首先,我們來瞭解一下淺拷貝。淺拷貝:不管多麼複雜的資料結構,淺拷貝都只會copy一層。下面就讓我們看一張圖,來瞭解一下淺淺拷貝的概念。

  看上面兩張圖,我們加入左圖表示的是一個列表sourcelist,sourcelist = [‘str1‘, ‘str2‘, ‘str3‘, ‘str4‘, ‘str5‘, [‘str1‘, ‘str2‘, ‘str3‘, ‘str4‘, ‘str5‘]];

  右圖在原有的基礎上多出了一個淺拷貝的copylist,copylist = [‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘,[‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘]];

  sourcelist和copylist表面上看起來一模一樣,但是實際上在記憶體中已經產生了一個新列表,copy了sourceLst,獲得了一個新列表,儲存了5個字串和一個列表所在記憶體的地址。

      我們看下面分別對兩個列表進行的操作,紅色的框框裡面是變數初始化,初始化了上面的兩個列表;我們可以分別對這兩個列表進行操作,例如插入一個值,我們會發現什麼呢?如下所示:

  

  從上面的代碼我們可以看出,對於sourceLst和copyLst列表添加一個元素,這兩個列表好像是獨立的一樣都分別發生了變化,但是當我修改lst的時候,這兩個列表都發生了變化,這是為什麼呢?我們就來看一張記憶體中的變化圖:

         

   我們可以知道sourceLst和copyLst列表中都儲存了一坨地址,當我們修改了source_lst1的元素時,相當於用‘sourceChange‘的地址替換了原來‘str1‘的地址,所以sourceLst的第一個元素髮生了變化。而copyLst還是儲存了str1的地址,所以copyLst不會發生改變。

  當sourceLst列表發生變化,copyLst中儲存的lst記憶體位址沒有改變,所以當lst發生改變的時候,sourceLst和copyLst兩個列表就都發生了改變。

  這種情況發生在字典套字典、列表套字典、字典套列表,列表套列表,以及各種複雜資料結構的嵌套中,所以當我們的資料類型很複雜的時候,用copy去進行淺拷貝就要非常小心。。。

  5.深拷貝

   深拷貝——即python的copy模組提供的另一個deepcopy方法。深拷貝會完全複製原變數相關的所有資料,在記憶體中產生一套完全一樣的內容,在這個過程中我們對這兩個變數中的一個進行任意修改都不會影響其他變數。下面我們就來實驗一下。

  

  看上面的執行結果,這一次我們不管是對直接對列表進行操作還是對列表內嵌套的其他資料結構操作,都不會產生拷貝的列表受影響的情況。我們再來看看這些變數在記憶體中的狀況:

  

  看了上面的內容,我們就知道了深拷貝的原理。其實深拷貝就是在記憶體中重新開闢一塊空間,不管資料結構多麼複雜,只要遇到可能發生改變的資料類型,就重新開闢一塊記憶體空間把內容複寫下來,直到最後一層,不再有複雜的資料類型,就保持其原引用。這樣,不管資料結構多麼的複雜,資料之間的修改都不會相互影響。這就是深拷貝。

本文參考部落格:https://www.cnblogs.com/Liubit/p/7668476.html,謝謝“Liubit”的分享!

 

Python資料結構之----資料存放區與深淺拷貝

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.