はじめに
pythonでリストの要素を消す際、clear関数を使用することがあると思います。今回、このclear関数でかなりつまづいたので、共有したいと思います。なお、環境はpython 3.7.3です。
今回行なっていたのは、リストを複数作成し、それぞれのリストにkeyを与えて辞書に追加するという作業です。具体的な処理は以下になります。
- 空のリストを作成
- リストに要素をいくつか追加
- 要素を追加したリストをvalueとし、keyを与えて辞書に追加
- リストを空にして2に戻る
上記処理の 4. の所でclear関数を使用していたのですが、この際 3. で登録した辞書のvalueからも要素が消えるという事態が発生しました。
簡単なコードで具体的な例を紹介したいと思います。
辞書にリストを追加
まず、リストを辞書に追加して、すぐに出力した結果です。特に問題なく辞書の中にリストが追加されています。
list = [1,2,3]
dic = {}
dic['key'] = list
print(dic)
#{'key': [1, 2, 3]}
#問題なし
リストの要素をclear関数で削除
続いて、リストの要素をclear関数で削除してから出力した結果です。辞書の中のリストが空になっています。
list = [1,2,3]
dic = {}
dic['key'] = list
print(dic)
#{'key': [1, 2, 3]}
list.clear()
print(dic)
#{'key': []}
#辞書の中のリストの要素が消えている
なぜこんなことになるのか?
どうも辞書のvalueにリストを追加する際、リストの値そのものではなく、リストというオブジェクトが追加されているようです。またこのオブジェクトはidで紐付けされています。このため同じオブジェクトidであるリストをclear関数で空にすると、同じく紐付けされた辞書中のリストも空になってしまいます。(うまく説明できません・・・・・)
なお、オブジェクトのidは以下のように確認できます。「list」と「dic中のvalue」が同じidになっていることが分かります。
list = [1,2,3]
dic = {}
dic['key'] = list
print(id(list))
#4381388680
print(id(dic['key']))
#4381388680
対策:空のリストを再代入する
この問題を解決する方法の一つとして、clear関数を使うのではなく、空のリストを再代入する方法があります。具体的には以下のようになります。
list = [1,2,3]
dic = {}
dic['key'] = list
print(dic)
#{'key': [1, 2, 3]}
print(id(dic['key']))
#4387610504
list = []
#list.clear()の代わりに、空のリストを代入する
print(id(list))
#4388425544 再代入によりidが変わる
print(dic)
#{'key': [1, 2, 3]}
#辞書の中のリストが消えない!!!
clear関数ではオブジェクトidが変化しないため、辞書中のオブジェクトまで要素が消えてしまいますが、空のリストを再代入する方法であればオブジェクトのidが変化するので、辞書中のオブジェクトは無傷となります。
また、この方法以外にもcopy関数を使って値自体を辞書に追加する方法があるようです。ご参考まで。 https://techblog.recochoku.jp/5158
まとめ
pythonでリストの要素をclear関数で消す場合、対象となるリストが辞書等にオブジェクトとして紐づいてしまっていると、紐づいている先のリストの要素まで消えてしまいます。こういった時はclear関数を使うのではなく、空のリストを再代入すると消したくない要素まで消えるのを防ぐことができます。
コメント