Python備忘録 多次元リストのデータ格納(置換)に関するプチ不思議(2022年11月)

 ある計算コードを作っているときに遭遇したPythonのプチ不思議です。

 例えば、入力が全てゼロの3×3の2次元のリストが既に存在するとします。

num_list = [[0, 0, 0], \

      [0, 0, 0], \

      [0, 0, 0]]

 以下のコードで前述の'num_list'を作成できますよね。

num_list = []

list_factor = [0, 0, 0]
for i in range(0,3):
    num_list.append(list_factor)

 num_listを出力します。

print("Initial num_list")
print(num_list)

 出力は以下のとおりです。

【出力】

Initial num_list [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

 これを以下のようなリストに置き換えたいとします。

num_list = [[1, 2, 3], \

      [4, 5, 6],  \

      [7, 8, 9]]

 このとき、FORTRANプログラミングの経験がある拙者としては、以下のようなPythonコードを組んでしまいます。

n = 1
for i in range(0,3):
    for j in range(0,3):
        num_list[i][j] = n
        n += 1

  置換後のnum_listを出力すると、

print("Final num_list")
print(num_list)

【出力】

Final num_list [[7, 8, 9], [7, 8, 9], [7, 8, 9]]

 となり、なぜか、

num_list = [[7, 8, 9], \

      [7, 8, 9], \

      [7, 8, 9]]

という2次元リストになってしまいます。

 ちょっと途中経過を表示してみます。


n = 1
for i in range(0,3):
    for j in range(0,3):
        num_list[i][j] = n
        n += 1
    print(f"{i}行目 {num_list[i]}")

print("Final num_list")
for list in num_list:
    print(list)

【出力】

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

Final num_list [7, 8, 9] [7, 8, 9] [7, 8, 9]

 forループの内側では想像通りに動いているのですが、最終的な数値はやはり

num_list = [[7, 8, 9], \

      [7, 8, 9], \

      [7, 8, 9]]

になっています。

 これはアドレスの問題では?と思い、早速、各行のアドレスを確認してみました。

n = 0
for list in num_list:
    print(f"メモリアドレス{id(list)}")

【出力】

メモリアドレス140079822994800 メモリアドレス140079822994800 メモリアドレス140079822994800

 やはりリストの各行に全て同じアドレスが割り当てられていました。

 この事象が発生する可能性がある場所はおそらく一番最初の3×3の2次元リストを作成するコードでは?と思い、ここでアドレスを参照してみました。

print("list_factorのメモリアドレス", id(list_factor))

【出力】

list_factorのメモリアドレス 140079822994800

 あ~~~、やっぱり。多次元リストはこういう初期化をしたらダメみたいです。

 そこで少々発想を転換し、以下のようなコードに見直すと、思うような二次元リストを作成できました。

n = 1
num_list = []
for i in range(0,3):
    list_factor = []
    for j in range(0,3):
        list_factor.append(n)
        n += 1
    num_list.append(list_factor)
    print(f"{i}行目 {num_list[i]}")

print("Final num_list")
for list in num_list:
    print(list)

【出力】

0行目 [1, 2, 3] 1行目 [4, 5, 6] 2行目 [7, 8, 9] Final num_list [1, 2, 3] [4, 5, 6] [7, 8, 9]

 これはPythonの文化・お作法の一つかなぁと感じました。







コメント