【Python】リストや辞書を複製するときは copy() しなきゃいけないし、それらに値として更にリストや辞書が含まれているなら copy.deepcopy() しましょう(自戒
はじめに
はまったのでメモします。
リストや辞書を代入で複製しようとするとハマる
a_dict の構造をそのまま b_dict でも流用して、値だけ b に変えたれ~と思ったとき、代入によってそれをしようとすると、 a_dict['id'] まで b に変わってしまいます。調べるに、どうやらこれが参照渡しというやつらしい。基本情報で出てきた(落ちたけど
>>> a_dict = {'id': 'a'} >>> a_dict {'id': 'a'} >>> b_dict = a_dict >>> b_dict {'id': 'a'} >>> b_dict['id'] = 'b' >>> b_dict {'id': 'b'} >>> a_dict {'id': 'b'} # ここは a のままでいてほしい >>>
解決策: copy() を使う
copy() を使うことで、参照渡しでなく値渡しをしましょう。
>>> a_dict = {'id': 'a'} >>> a_dict {'id': 'a'} >>> b_dict = a_dict.copy() >>> b_dict {'id': 'a'} >>> b_dict['id'] = 'b' >>> b_dict {'id': 'b'} >>> a_dict {'id': 'a'} >>>
copy() で複製しようとしたリストや辞書の値に、リストや辞書が含まれているとハマる
今度は a_dict['pattern'] に配列を加えてから、 copy() で b_dict を作る。で、配列の内容を b っぽいものに変更すると、 a_dict['pattern'] も b っぽい内容に……なったはずなんだけど、ならない。 Python のバージョンが上がって(以下の REPL は 3.8.1) 、挙動が変わったのかな?
>>> a_dict = {'id': 'a', 'pattern': ['a', 'A']} >>> a_dict {'id': 'a', 'pattern': ['a', 'A']} >>> b_dict = a_dict.copy() >>> b_dict {'id': 'a', 'pattern': ['a', 'A']} >>> b_dict['id'] = 'b' >>> b_dict['pattern'] = ['b', 'B'] >>> b_dict {'id': 'b', 'pattern': ['b', 'B']} >>> a_dict {'id': 'a', 'pattern': ['a', 'A']} # a_dict['pattern'] も ['b', 'B'] になっちゃってた気がするんだが
……まあいいや、なったとして。それはガワの辞書は copy() によって値渡しできてるけど、中身のリストや辞書は参照渡しされているからみたいです。 Python のリファレンス中ではそれぞれ「浅いコピー」「深いコピー」と呼ばれていて、今回みたいにリストや辞書(クラスなども)が入れ子になっているオブジェクトを「複合オブジェクト」と呼ばれています。
解決策: copy.deepcopy() を使う
import copy
して、 b_dict = copy.deepcopy(a_dict)
すると解消します。
...
コピーって、本当に奥が深いですネ!
shinobe179拝