Python 如何正確初始化二維陣列

最近的開發刷題需求中,遇到需要宣告二維陣列的情境,卻發現資料的存取結果與預期不符。
資料怎麼塞都不對…🥹

在這篇文章中,我將分享如何正確地初始化 Python 二維陣列。

問題

故事的開頭是這樣的,一切就是這麼簡潔這麼美,如此的 Pythonic ☺️

1
test_list = [[]] * 3

但結果發現無論我在哪個維度塞資料時,都發現所有的子陣列都會出現相同的結果。

1
2
3
4
5
test_list = [[]] * 3
test_list[0].append(0)

for l in test_list:
print(l)

執行結果

1
2
3
[0]
[0]
[0]

WTF?
image 16

為了探究這個問題,我使用 id() 函數列出了每個子陣列的物件 ID。

  • id(object)
    Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
    CPython implementation detail: This is the address of the object in memory.

  • https://docs.python.org/3/library/functions.html#id

1
2
3
4
test_list = [[]] * 3

for l in test_list:
print(id(l))

結果發現

1
2
3
4307825408
4307825408
4307825408
image 17

Why? 到底是為什麼?

結論

經過一番追尋之後,終於在 Python 官方文件 Sequence Types 找到答案。

其中,有一段很重要的提醒。

Note that items in the sequence s are not copied; they are referenced multiple times. This often haunts new Python programmers;

這段話解釋了為什麼我們之前看到所有子陣列都有相同的 ID —— 它們實際上是指向同一個物件的多個參考。

同時官方文件也提供了發生相同問題的程式碼。

1
2
3
4
5
6
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

同時也推薦各位用以下方式初始化。

1
2
3
4
5
6
>>> lists = [[] for i in range(3)]
>>> lists[0].append(3)
>>> lists[1].append(5)
>>> lists[2].append(7)
>>> lists
[[3], [5], [7]]

如果你需要建立特定大小的二維陣列(y 列 x 行),可以這樣做:

1
2
3
4
5
6
7
y = 3
x = 4

test_list = [[0] * x for _ in range(y)]

# test_list[y][x]
test_list[y - 1][x - 1] = 1

而官方也出了一篇 FAQ 專門講這件事
How do I create a multidimensional list?

追尋至此終於宣告破案…

也許你也會想看看