Python 巢狀串列中使用 * 和 + 的陷阱:新手指南

更新日期: 2024 年 9 月 30 日

在 Python 編程中,串列是一種非常常用的數據結構,允許我們存儲和操作多個元素。

當我們處理巢狀串列(即串列中的元素也是串列)時,操作可能會變得有點複雜。

特別是,使用乘法運算符 * 和加法運算符 + 時,可能會引發一些新手不易察覺的陷阱。

本文將詳細介紹在巢狀串列中使用 *+ 的潛在問題,幫助你避免常見的錯誤。


巢狀串列的基本概念

什麼是巢狀串列?

巢狀串列是指串列中的元素本身也是串列。

這種結構允許我們創建多維數據結構,如矩陣或多維陣列。

示例:

# 一個 2x3 的矩陣
matrix = [
    [1, 2, 3],
    [4, 5, 6]
]

使用 * 運算符的陷阱

基本用法

在串列中使用 * 運算符,可以重複串列中的元素。

示例:

list1 = [0] * 3
print(list1)  # 輸出:[0, 0, 0]

在巢狀串列中使用 *

當我們想要創建一個多維串列,例如 3×3 的矩陣,我們可能會嘗試:

matrix = [[0] * 3] * 3
print(matrix)
# 輸出:
# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

看起來似乎正確,但實際上這裡存在陷阱。

問題的根源:淺拷貝導致的引用共享

當你使用 * 來創建巢狀串列時,內部的子串列,實際上是同一個物件的多個引用。

驗證:

matrix = [[0] * 3] * 3
print(matrix[0] is matrix[1])  # 輸出:True
print(matrix[1] is matrix[2])  # 輸出:True

解釋:

  • matrix[0]matrix[1]matrix[2] 都是同一個串列物件的引用。
  • 這意味著對其中一個子串列的修改,會影響到所有子串列。

問題示例

matrix = [[0] * 3] * 3
matrix[0][0] = 1
print(matrix)

預期輸出:

[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

實際輸出:

[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

解釋:

  • 因為所有子串列都是同一個物件的引用,修改一個子串列中的元素,其他子串列也會同步修改。

解決方案:使用串列推導式創建獨立的子串列

matrix = [[0] * 3 for _ in range(3)]
matrix[0][0] = 1
print(matrix)

輸出:

[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

解釋:

  • 使用串列推導式,確保每個子串列都是一個新的串列物件,而不是同一個引用。

深入理解

  • [0] * 3 創建了一個包含三個 0 的串列。
  • [[0] * 3] * 3 創建了一個包含三個相同子串列引用的串列。
  • 串列推導式 [[0] * 3 for _ in range(3)] 則創建了三個獨立的子串列。

使用 + 運算符的陷阱

基本用法

使用 + 運算符可以將兩個串列合併。

示例:

list1 = [1, 2]
list2 = [3, 4]
combined = list1 + list2
print(combined)  # 輸出:[1, 2, 3, 4]

在巢狀串列中使用 +

當我們嘗試在巢狀串列中使用 + 時,需要注意操作物件的類型。

問題示例

matrix = [[1, 2, 3], [4, 5, 6]]
new_row = [7, 8, 9]
matrix = matrix + new_row
print(matrix)

預期輸出:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

實際輸出:

[[1, 2, 3], [4, 5, 6], 7, 8, 9]

解釋:

  • matrix 是一個包含串列的串列。
  • new_row 是一個串列。
  • 使用 +matrixnew_row 合併,會將 new_row 的元素逐個添加到 matrix 中,而不是作為一個整體的子串列。

解決方案:將新元素包裝為串列

matrix = [[1, 2, 3], [4, 5, 6]]
new_row = [7, 8, 9]
matrix = matrix + [new_row]
print(matrix)

輸出:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

解釋:

  • new_row 放入一個串列中,確保它作為一個整體被添加到 matrix 中。

使用 append() 方法

另一種解決方案是使用串列的 append() 方法。

matrix = [[1, 2, 3], [4, 5, 6]]
new_row = [7, 8, 9]
matrix.append(new_row)
print(matrix)

輸出:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

總結常見陷阱與避免方法

使用 * 創建巢狀串列時的陷阱

  • 陷阱:使用 * 創建的巢狀串列,內部子串列是同一個物件的引用。
  • 後果:修改一個子串列,其他子串列也會被修改。
  • 避免方法:使用串列推導式創建獨立的子串列。

示例:

# 錯誤的做法
matrix = [[0] * 3] * 3

# 正確的做法
matrix = [[0] * 3 for _ in range(3)]

使用 + 合併串列時的陷阱

  • 陷阱:在巢狀串列中直接使用 + 會展開被添加的串列的元素。
  • 後果:巢狀結構被破壞,元素被扁平化。
  • 避免方法:將被添加的串列放入一個新的串列中,或者使用 append() 方法。

示例:

# 錯誤的做法
matrix = [[1, 2, 3], [4, 5, 6]]
new_row = [7, 8, 9]
matrix = matrix + new_row  # 錯誤

# 正確的做法
matrix = matrix + [new_row]
# 或者
matrix.append(new_row)

結論

  • 理解串列的引用機制:在 Python 中,串列是可變物件,對其進行賦值或使用 * 複製時,需要注意是否創建了新的物件還是僅僅複製了引用。
  • 使用串列推導式創建巢狀串列:這是避免引用共享問題的有效方法。
  • 在合併巢狀串列時小心使用 + 運算符:確保被添加的元素保持巢狀結構,可以使用 append() 方法或將其包裝為串列。

延伸閱讀

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *