Python列表的深淺複製

昀溪發表於2018-08-20

概述

Python的列表可以複製,但是這裡面有淺複製和深複製,我相信有些人不明白什麼是深複製和淺複製,今天我們就來談談。

= 號複製

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

list1 = ["A", "B"]

list2 = list1

print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))
list2.append("C")
print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))

透過在list2中新增一個元素,檢視執行結果發現兩個列表都變化了。 所以 = 號是列表最簡單的複製,其實不能算複製,因為你看兩個列表的ID相同。

列表函式copy()複製

下面我們換一種複製方式

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

list1 = ["A", "B"]

list2 = list1.copy()

print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))
list2.append("C")
print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))

這裡才是複製是真的複製了,因為它們的ID不同。檢視執行結果發現只有list2發生變化,但它是淺複製。

[:]複製

我們再看另外一種複製方式。其實嚴格上說切片不是複製,列表切片後會返回新列表,原列表不變。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

list1 = ["A", "B"]

list2 = list1[:]

print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))
list2.append("C")
print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))

執行結果和上面一樣

上面演示了3個例項,但是屬於淺複製的只有第2種複製方式,你可能問,都已經真的複製了那怎麼還有深複製呢?

列表結構變化演示

這裡我們依然使用copy函式,這個函式和使用[:]是一樣的,下面有2處變化

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

# 在列表中新增一個列表元素
list1 = ["A", "B", [1, 2]]

list2 = list1[:]

print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))
# 這裡我們修改列表中的列表,對內層列表進行修改
list2[2].append("C")
print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))

檢視執行結果,你發現內層列表都修改了。你沒看錯。繼續向下看。

那如果在這種列表巢狀的場景中也真的複製成獨立互不干涉的怎麼辦呢?這就用到深複製。

deepcopy函式

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

# 匯入該模組
import copy

# 在列表中新增一個列表元素
list1 = ["A", "B", [1, 2]]

# 深複製
list2 = copy.deepcopy(list1)

print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))
# 這裡我們修改列表中的列表,對內層列表進行修改
list2[2].append("C")
print("list1: ", list1, "ID: ", id(list1))
print("list2: ", list2, "ID: ", id(list2))

檢視執行結果,符合預期。

什麼是淺複製和深複製以及兩者區別是什麼

其實從效果上看你應該以及明白了兩者區別,但要說這個問題就要說資料型別,資料型別分兩類:

  • 基本資料型別,包括字串、數字、布林。儲存的是真實資料。
  • 物件資料型別(引用資料型別),就是物件,類的例項,儲存的是物件的引用。

賦值語句 = 意味著什麼呢?比如 name = "tom" 賦值語句(=)的作用就是將變數名和一個記憶體物件進行繫結,如果物件存在就直接繫結,如果不存在就先建立在繫結。name儲存的是指向"tom"的指標,而儲存tom的記憶體區域儲存的就是tom這個真實資料;list1 = [1, 2] 這種list1儲存的是列表物件的指標,但是列表物件本身並不儲存資料而是對其他物件的引用,這裡的列表包含1和2兩個物件,這兩個物件是在記憶體真實存在的,列表物件只是說引用了這些物件而已,列表也僅僅是一種資料的組織形式。

淺複製:複製指標指向,資料在記憶體中還只是一份,但是隻複製一級物件,如果再有一個引用資料型別就不行了

深複製:可以理解為遞迴淺複製

如果還不明白就看下面的圖

下圖是使用深度複製後的圖示

 

總結

要想生成兩個互不干涉的列表就用deepcopy,其他情況任選。

相關文章