1. 程式人生 > >淺拷貝和深拷貝詳解

淺拷貝和深拷貝詳解

官方文件:

  • A shallow copy constructs a new compound object and then (to the extent possible) inserts the same objects into it that the original contains.
  • A deep copy constructs a new compound object and then, recursively,inserts copies into it of the objects found in the original.

在python中,物件賦值實際上是物件的引用。

  • 淺拷貝: 拷貝物件的值,但不拷貝物件內部元素的值,只拷貝內部元素的引用

    >>> SL = {'name': 'SL',  'friend': ['A', 'B', 'C']}
    >>> YS = SL.copy()
    

    檢視SL,YS的id值

    >>> print id(SL), id(YS)
    139783840481360 139783840476248
    

    可以看出,兩者的id值是不同的,說明拷貝物件的值。
    接下來檢視SL[‘name’],YS[‘name’] 以及 SL[‘friend’],YL[‘friend’]的id值。

    >>> 
    print id(SL['name']), id(YS['name']) 139783840488224 139783840488224 >>> print id(SL['friend']), id(YS['friend']) 139783840438608 139783840438608

    內部元素的id值分別對應相同,說明都是指向相同物件。
    下面對SL作出修改,然後檢視SL和YS的狀態

    >>> SL['name'] = 'SS'
    >>> SL['friend'][0] = 'Z'
    >>> SL
    {'name': 'SS', 'friend'
    : ['Z', 'B', 'C']} >>> YS {'name': 'SL', 'friend': ['Z', 'B', 'C']}

    SL[‘name’]指向字串(不可變物件),SL[‘friend’]指向是列表(可變物件)。
    可以看出,在替換SL中的不可變物件時,YS不受影響,修改SL中的可變物件時,YS也會相應改變。
    分析:
    SL[‘name’],YS[‘name’]原本指向相同物件,由於SL[‘name’]指向的是不可變物件。所以當改變SL[‘name’]時,會建立一個新的字串物件,並使SL[‘name’]指向它,YS[‘name’]仍然指向原本物件。
    SL[‘frined’],YS[‘friend’]指向相同物件(即列表),屬於可變物件。因而修改SL[‘friend’]時,不需要建立新物件,直接修改SL[‘friend’]指向的列表。

    檢視一下改變之後SL[‘name’],YS[‘name’]和SL[‘friend’],YS[‘friend’]的id值

    >>> print id(SL['name']), id(YS['name'])
    139783774899256 139783840488224
    >>> print id(SL['friend']), id(YS['friend'])
    139783840438608 139783840438608
    

    前兩者的id值已經不同,後兩者的id值相同,由此可見,上述分析是正確的。

因此,淺拷貝會得到一個新的物件,但對於物件內部的元素,無論是可變物件還是不可變物件都只拷貝引用

  • 深拷貝: 拷貝物件的值,拷貝不可變元素的引用,拷貝可變元素的值

    >>> SL = {'name': 'SL',  'friend': ['A', 'B', 'C']}
    >>> YS = deepcopy(SL)

    檢視SL,YS的id值

    >>> print id(SL), id(YS)
    139732549292672 139732549294912
    

    兩者的id值不同,說明拷貝物件的值。接下來檢視SL[‘name’],YS[‘name’] 以及 SL[‘friend’],YL[‘friend’]的id值。

    >>> print id(SL['name']), id(YS['name'])
    139732549298976 139732549298976
    >>> print id(SL['friend']), id(YS['friend'])
    139732549249360 139732549249216
    

    前兩者的id值相同,後兩者的id值不同。這裡也對SL作出修改,然後檢視SL和YS的值

    >>> SL['name'] = 'SS'
    >>> SL['friend'][0] = 'Z'
    >>> SL
    {'name': 'SS', 'friend': ['Z', 'B', 'C']}
    >>> YS
    {'name': 'SL', 'friend': ['A', 'B', 'C']}
    

    不管對SL作出怎麼的修改都不會影響到YS的值
    分析:
    修改SL[‘name’]時,同上
    SL[‘frined’],YS[‘friend’]原本就指向不同物件,修改SL[‘friend’]時,明顯不會影響到YS的值。

    下面檢視一下改變之後SL[‘name’],YS[‘name’]和SL[‘friend’],YS[‘friend’]的id值

    >>> print id(SL['name']), id(YS['name'])
    139732549296616 139732549298976
    >>> print id(SL['friend']), id(YS['friend'])
    139732549249360 139732549249216
    

    意料之中,仍然對應不相同

    因此,深拷貝也會得到一個新的物件,對於物件的內部元素,拷貝不可變物件的引用,拷貝可變物件的值

總而言之,淺拷貝得到的副本,修改副本內部的可變物件時會影響到原始物件;深拷貝得到的副本 ,無論如何修改副本都不會影響到原始物件。

注:淺拷貝和深拷貝只有在關於複合物件(包含其它物件的物件,列表,類等)才有區別。對於不可變物件,沒有被拷貝的說法,即使用deepcopy,檢視id值也是一樣的。