LoginSignup
11
4

More than 3 years have passed since last update.

Pythonの二次元配列の罠【配列のコピー】

Last updated at Posted at 2020-03-10

基本的なことなんだろうけど、理解してないせいで詰まったのでメモ。

まえがき

pythonで配列を作るのは簡単でいいですね。

a = [0] * 2
print(a) # [0, 0]

二次元配列も作ってみましょう。

b = [[0] * 2] * 2
print(b) # [[0, 0], [0, 0]]

あら便利。じゃあ操作してみましょう。

b[0][1] = 1

printしてみましょう。

print(b) #[[0, 1], [0, 1]]

んん??????

結論

こう書きましょう。

c = [[0] * 2 for i in range(2)]
print(c) # [[0, 0], [0, 0]]
c[0][1] = 1
print(c) # [[0, 1], [0, 0]]

なんでダメなの?

id()関数を使ってみましょう。

b = [[0] * 2] * 2
print(id(b[0])) # 1835216839944
print(id(b[1])) # 1835216839944 アドレスが同じ
c = [[0] * 2 for i in range(2)]
print(id(c[0])) # 1835216058120
print(id(c[1])) # 1835216840392 アドレスが違う

*2でコピーすると同じアドレスを参照することになります。

b[0][1] = 1、という形でアドレスの参照している「中身」を変更すると、アドレスを共有する全ての配列に反映されるんですね。

なんで1次元配列だとこうならないの?

1次元配列だとこの現象は発生しませんね?

a = [0] * 2
a[0] = 1
print(a) # [1, 0]

アドレスを見てみましょう。

a = [0] * 2
print(a[0] == a[1]) # True
a[0] = 1
print(a[0] == a[1]) # False
print(a) # [1, 0]

最初にコピーした時点では同じアドレスを持っていますが、a[0]=1を代入するとアドレスが変化しました。

これは参照先がイミュータブル(変更不能体)かミュータブル(変更可能体)かの違いです。

1次元配列を操作する時は「整数を持つ参照先の中身を変更することはできない」(イミュータブル)ので、別のアドレスを与えることで実質的に値を変更しています。

2次元配列を操作する時は「配列を持つ参照先の中身は変更できる」のでアドレスは固定したまま値を変更することができます。

ちなみに

こんな代入だとどうでしょう?

# さっきまでのパターン
b = [[0] * 2] * 2
b[0][1] = 1
print(b) # [[0, 1], [0, 1]]
print(id(b[0]) == id(b[1])) #True

# 変化させたバージョン
b = [[0] * 2] * 2
b[0] = [0, 1]
print(b) # [[0, 1], [0, 0]]
print(id(b[0]) == id(b[1])) #False

新たにアドレスが生成されます。
この場合は「中身の操作」ではなく「再定義」という扱いになり、参照先が変更されます。

b = [[0] * 2] * 2
b[0] = [0, 0]
print(b) # [[0, 0], [0, 0]]
print(id(b[0]) == id(b[1])) #False

参考など

参考: http://delta114514.hatenablog.jp/entry/2018/01/02/233002

追記:
コメントの指摘を元に内容を修正しました。「Pythonは全て参照渡し」「イミュータブル(変更不能体)とミュータブル(変更可能体)」あたりの概念を理解してませんでした。
参考: https://snowtree-injune.com/2019/09/16/shallow-copy/

11
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4