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
是一個串列。- 使用
+
將matrix
和new_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()
方法或將其包裝為串列。