python中的深淺copy shallow copy和 deep copy

python中copy是分爲淺copy和深copy

shallow copy

從新分配一塊內存,建立一個新的對象,裏面的元素是被拷貝對象中子元素的引用。python

- 特色:會建立新的對象,這個對象並不是是原對象的引用,而是原對象內第一層子元素對象的引用。

import copy

# L1 對象內部爲兩個元素: index 0 :[1,2],    index 1:(100,200)
L1 = [[1, 2], (100, 200)]
# 對L1進行淺copy ,此時,獲得一個新的List對象,並賦值給L2,
L2 = list(L1)

print("L1的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))

# L1的內存地址爲--2581953960968,第一個元素的地址--2581953961032,第二個元素的地址--2581951586568
# L2的內存地址爲--2581953936904,第一個元素的地址--2581953961032,第二個元素的地址--2581951586568

# 修改共同引用的列表的內容,因爲L1,L2的第一個元素都指向這個列表,所以,L1,L2 對應的元素內容 都發生了變化,可是id是不變的。
L1[0].append(3)
print(L1)  # [[1, 2, 3], (100, 200)]
print(L2)  # [[1, 2, 3], (100, 200)]

# L1 新增元素 ,L1 和L2  互相獨立,不受影響
L1.append(100)
print(L1)  # [[1, 2, 3], (100, 200), 100]
print(L2)  # [[1, 2, 3], (100, 200)]

# 元組不可變,所以,元組使用 + ,獲得的是一個新的元祖對象,所以L2內的元組對象id不變,L1內的元組對象id發生了變化。
L1[1] += (500, 600)

print(L1)   # [[1, 2, 3], (100, 200, 500, 600), 100]
print(L2)   # [[1, 2, 3], (100, 200)]
print("L1的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的內存地址爲--2581953960968,第一個元素的地址--2581953961032,第二個元素的地址--2581952542584
# L2的內存地址爲--2581953936904,第一個元素的地址--2581953961032,第二個元素的地址--2581951586568
# 列表是可變的,+ 號操做,在以前基礎上新增元素,相似列表的extend方法
L1[0] += [3000, 4000]
print(L1)   # [[1, 2, 3, 3000, 4000], (100, 200, 500, 600), 100]
print(L2)   # [[1, 2, 3, 3000, 4000], (100, 200)]
print("L1的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的內存地址爲--2581953960968,第一個元素的地址--2581953961032,第二個元素的地址--2581952542584
# L2的內存地址爲--2581953936904,第一個元素的地址--2581953961032,第二個元素的地址--2581951586568
  • 實現方式:app

    • 該對象類型的工廠函數,
    • 切片操做(對部分類型對象有效)
    • copy模塊中的copy函數
L1=[[100,200],(1000,2000)]
L3=list(L1)
L4=L1[:]
L5=copy.copy(L1)
print("L3的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L3), id(L3[0]), id(L3[1])))
print("L4的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L4), id(L4[0]), id(L4[1])))
print("L5的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L5), id(L5[0]), id(L5[1])))
# L3的內存地址爲--2581952584392,第一個元素的地址--2581953960904,第二個元素的地址--2581951626888
# L4的內存地址爲--2581953960968,第一個元素的地址--2581953960904,第二個元素的地址--2581951626888
# L5的內存地址爲--2581953936968,第一個元素的地址--2581953960904,第二個元素的地址--2581951626888
  • 注意:對於元組,淺拷貝並不會建立新的內存,而是讓新的變量指向被拷貝對象的內存地址。
s1=(100,200)
s2=tuple(s1)
s3=s1[:]
s4=copy.copy(s1)
print(id(s1))   # 2399341093128
print(id(s2))   # 2399341093128
print(id(s3))   # 2399341093128
print(id(s4))   # 2399341093128

deepcopy 深複製

從新分配一塊內存,建立一個新對象,並將被拷貝對象中的全部元素,以遞歸的方式,複製到這個新對象中。新對象和原對象徹底獨立,互不影響。函數

import  copy

L1=[[1,2],(100,200)]

L2=copy.deepcopy(L1)
print("L1的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的內存地址爲--{},第一個元素的地址--{},第二個元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的內存地址爲--1665896340552,第一個元素的地址--1665896340616,第二個元素的地址--1665893966024
# L2的內存地址爲--1665896340488,第一個元素的地址--1665896340808,第二個元素的地址--1665893966024

L1[0].append(3)
print(L1)   # [[1, 2, 3], (100, 200)]
print(L2)   # [[1, 2], (100, 200)]


L1.append(1000)
print(L1)   # [[1, 2, 3], (100, 200), 1000]
print(L2)   # [[1, 2], (100, 200)]
- 注意:在這裏,L1和L2中的元組的內存地址是同樣的,可是並不影響, 由於元組屬於不可變對象,沒法進行其餘操做來改變。能夠共享。
- 特殊狀況:
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
print(l1) # [[1, 2, 3], (30, 40), 100]
print(l2) # [[1, 2], (30, 40)]

這個例子,列表 x 中有指向自身的引用,所以 x 是一個無限嵌套的列表。可是深度拷貝 x 到 y 後,程序並無出現 stack overflow 的現象。這是由於深度拷貝函數 deepcopy 中會維護一個字典,記錄已經拷貝的對象與其 ID。拷貝過程當中,若是字典裏已經存儲了將要拷貝的對象,則會從字典直接返回。code

def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)   # 查詢被拷貝對象x的id
    y = memo.get(d, _nil)   # 查詢字典裏是否已經存儲了該對象
    if y is not _nil:   
        return y    # 若是字典裏已經存儲了將要拷貝的對象,則直接返回
相關文章
相關標籤/搜索