kakuyonの日記

Webアプリやセキュリティに関する事を書いていけたらと考えています。

Pythonで配列のコピー作る時に用心すべきこと

自身への戒めとして書いときます



Pythonで変数にリストを代入しようとすると、
変数にはリストへの参照が渡されるようになっています。

>>> hoge = ['a', 'b', 'c']
>>> fuga = hoge
>>> fuga[1] = 'change'
>>> print(hoge)
['a', 'change', 'c']


片方だけの値を変えたい場合、リストへの参照ではなく、
リストのコピーを渡す必要があります。いわゆる値渡しです。

>>> hoge = ['a', 'b', 'c']
>>> fuga = list(hoge)  # あるいはhoge[:]
>>> fuga[1] = 'change'
>>> print(hoge)
['a', 'b', 'c']


list()の引数にコピーしたいリストを与える、
もしくはスライスを利用するのもok



本題

しかし、二次元配列だとこうはいきません。いけませんでした

>>> hoge = [['a', 'b', 'c'], ['d', 'e', 'f']]
>>> fuga = list(hoge)
>>> fuga[0][1] = 'change'
>>> print(hoge)
[['a', 'change', 'c'], ['d', 'e', 'f']]


list()を使おうが使うまいがこうなります。
hoge[:][:]とかも無意味です。
一次元目まではコピーしてくれますが、
二次元目以降は参照渡しになるらしいです(この数え方合ってる?)



対策

copyモジュールというものを使用します。
このモジュールに存在するdeepcopy()というメソッドで解決できます

>>> import copy
>>> hoge = [['a', 'b', 'c'], ['d', 'e', 'f']]
>>> fuga = copy.deepcopy(hoge)
>>> fuga[0][1] = 'change'
>>> print(hoge)
[['a', 'b', 'c'], ['d', 'e', 'f']]


モジュールに頼りたくないなら、内包表記でしょうか

>>> hoge = [['a', 'b', 'c'], ['d', 'e', 'f']]
>>> fuga = [x[:] for x in hoge]
>>> fuga[0][1] = 'change'
>>> print(hoge)
[['a', 'b', 'c'], ['d', 'e', 'f']]


二次元配列でしか試していませんが、これは多次元配列であれば
同じことが言えると思います。



Pythonでは基本参照渡し、
二次元配列の値渡しはdeepcopy()を使う
この事を忘れると数時間が水の泡になったりするので気を付けましょう




終わり