Python 靈異事件:為什麼我只改了一個格子,整排都跟著變了?

2026-04-06 11:08 | By justin | python
(Updated: 2026-04-06 11:08)

Python 靈異事件:為什麼我只改了一個格子,整排都跟著變了?

如果你嘗試在 Python 中建立一個 3x3 的九宮格(二維列表),你可能會很直覺地寫出這行程式碼:

# 建立一個 3x3 的二維列表,初始值都是 0
grid = [[0] * 3] * 3

看起來很完美對吧?印出來也是漂亮的 [[0, 0, 0], [0, 0, 0], [0, 0, 0]]。但神奇的事情發生了,當你只想把「左上角」的格子改成 1 時:

grid[0][0] = 1
print(grid)

預期結果: [[1, 0, 0], [0, 0, 0], [0, 0, 0]] 實際結果: [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

「等一下,我明明只改了第一列,為什麼每一列的第一個數字都變成了 1?」別驚慌,這不是 Python 的 Bug,而是它為了節省體力,在背後玩了一個「分身術」。


核心祕密:它是「同一個房間」的三把鑰匙

在上一篇我們聊過 is== 的差別,知道 Python 的變數有時候是指向同一個記憶體位址。這個問題的根源就在這裡。

當你寫 [0] * 3 時,Python 確實幫你建立了三個獨立的 0,變成 [0, 0, 0]。 但當你接著寫 [...] * 3 時,Python 為了偷懶(優化效能),它並不會真的去「影印」三份內容一模一樣的列表,而是複製了三份「遙控器」

想像一下: 1. 你先蓋了一個房間(內層列表:[0, 0, 0])。 2. 你把這個房間重複了三次放在一個大箱子裡。 3. 關鍵點: Python 並沒有蓋出三間長得一樣的房間,它只是給了你三把「同一間房間」的鑰匙。

所以,不論你用哪一把鑰匙進去改裝房間(修改 grid[0]grid[1]grid[2]),因為大家進去的都是同一個房間,所以當你從另一扇門看進去時,房間已經被改掉了。


為什麼 [0] * 3 就不會出事?

你可能會問:「那為什麼 [0, 0, 0] 裡面的三個 0 不會互相影響?」

這是因為 Python 中的數字(Integer)是「不可變的」(Immutable)。當你把第一個 0 改成 1 時,Python 並不是把 0 抹掉寫成 1,而是直接把那格的位置指向一個新的數字 1

列表(List)是「可變的」(Mutable)。當你執行 grid[0][0] = 1 時,你是進入了那個列表「內部」去修改內容,而所有指向這個列表的變數,都會看到這個變動。


那該怎麼辦?正確的「影印」方式

如果你想要建立一個互不干擾、各自獨立的九宮格,你需要讓 Python 真的去「蓋三間新房子」。最常見的做法是使用 「列表生成式 (List Comprehension)」

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

這行程式碼的意思是:「請幫我跑 3 次迴圈,每一次都重新建立一個全新的 [0, 0, 0] 列表。」

這樣一來,每一列都是獨立的實體,你有三間獨立的房間,改了第一間,第二、三間絕對不會跟著變。


總結

[[0] * 3] * 3 是一個非常經典的 Python 陷阱。它教了我們重要的一課:

  • 乘法運算子 * 用在列表時,複製的是「引用(Reference)」,而不是內容。
  • 如果你處理的是會被修改的對象(像是列表、字典),請改用 迴圈列表生成式 來確保每個元素都是獨立的。

下次看到你的資料「牽一髮而動全身」時,記得檢查一下,你是不是不小心給了 Python 太多的「分身鑰匙」囉!


0 留言

目前沒有留言

發表留言
回覆