リストのコピーにハマる
Pythonでリストの複製を行おうと思って以下のようなコードを書いてハマる。
sequence = [2, 3, 5, 7, 11, 13]
subsequence = sequence
subsequence.remove(13)
print(sequence)
print(subsequence)
期待していた実行結果は
[2, 3, 5, 7, 11, 13]
[2, 3, 5, 7, 11]
なのだが、実際の結果は
[2, 3, 5, 7, 11]
[2, 3, 5, 7, 11]
ハテ? removeしたsubsequenceの方はともかく、なんでsequenceまで要素13が削除が削除されたの?
理由が分からなくて調べるとすぐに判明。subsequence = sequence
というのが誤りで、これだとsubsequenceのIDをsequenceのIDで上書きしてしまう。つまり、subsequenceが指し示しているものと、sequenceが指し示しているものが、同一のものになるのだそうだ。だから、subsequenceにsequenceのコピーを作って内容を書き換えたつもりだったが、sequenceの内容そのものを書き換えていたのだ。
この辺は、C言語のポインタで出てくるような話に似ている。関数呼び出しの場合に参照渡し(アドレス渡し?ポインタ渡し?)すると呼び出し元の変数にアクセスできるようになるものだ。swap()
関数のようなものをCで書く場合に良く例として挙げられるものだ。
ちなみに、上のPythonコードを期待通りに動作させる場合は、リストのIDをコピーするのではなく各要素をコピーするようにスライス[:]
を付けてあげると良いのだそうだ。
sequence = [2, 3, 5, 7, 11, 13]
subsequence = sequence[:]
subsequence.remove(13)
print(sequence)
print(subsequence)
ただし、このコピー操作はとても遅いのか、数万個要素のリストコピーを数万回のループで処理させていたらとても時間がかかっていたのに、その分を見直して削除したら途端に早くなった。長い系列処理で、こういうコードを繰り返し実行するのは避けるべきだ。