あるリストの内容をコピーするには、copyメソッドを使用する。このメソッドにはパラメーターはなく、その戻り値は元のリストの「浅いコピー」となる。「浅いコピー」については後で少し見る。
以下に例を示す。
strlist1 = ['foo', 'bar', 'baz']
strlist2 = strlist1.copy() # 浅いコピーの作成
strlist3 = strlist1 # intlist1とintlist3は同じリストオブジェクトを参照する
print(strlist1)
print(strlist2)
print(id(strlist1))
print(id(strlist2))
print(id(strlist3))
実行結果を以下に示す。
実行結果を見ると、変数strlist1とstrlist3のアイデンティティーは「57880352」となっているので、これらは同じリストを参照していることが分かる(「strlist3 = strlist1」としているので当然だ)。一方、copyメソッドで作成したリストを参照している変数strlist2のアイデンティティーは「57633592」となっていて、元のリストとは別物であることも分かる。
変数strlist1とstrlist3は同じリストを参照しているので、どちらかの要素を変更すれば、もう一方の要素も変更される。
strlist1[0] = 'FOO'
print(strlist3) # ['FOO', 'bar', 'baz']
実行結果を以下に示す。
strlist1の要素を変更したのに、print関数でstrlist3の内容を表示すると、その要素が変わっていることに注目しよう。
これに対して、例えばstrlist2[0]を変更しても、それは「strlist[0]という名札を付け替える」ことなので、strlist1やstrlist3には変更は及ばない。
strlist2[0] = 'Foo'
print(strlist1)
print(strlist2)
実行結果は次の通りだ。
ここまでの話は比較的分かりやすいはずだ。変数strlist1と変数strlist2はそれぞれ別のリストオブジェクトを参照しているので、strlist1[0]やstrlist2[0]は名札としては別々のものとなっている。
では、次のような場合はどうだろう。
intlist1 = [[1, 2], [3, 4], [5, 6]]
intlist2 = intlist1.copy()
print(intlist1)
print(intlist2)
print(id(intlist1))
print(id(intlist2))
この場合も、intlist1とintlist2の内容は同じで、アイデンティテイーは異なるので、copyメソッドにより新しくリストが作成されたことが分かる。
次に、intlist1[0][0]の値を変更してみよう。
intlist1[0][0] = 101
print(intlist1)
print(intlist2)
実行結果を以下に示す。
意外なことに、intlist2[0][0]の値も変更されてしまった。これが「浅いコピー」が持つ特徴の一つだ。なぜこうなってしまったのかを少し考えてみよう。ポイントは「浅いコピー」とは「オブジェクトへの参照をコピーする」という点だ。
まず、intlist1[0]はリスト「[1, 2]」というリストを参照している。そして、その浅いコピーであるintlist2[0]でも同じ「[1, 2]」というリストを参照しているのである。つまり、intlist1とintlist2の要素は次のように同一のオブジェクトを参照している。
ここでintlist1[0]へ代入をすれば、名札の付け替えが行われるので、intlist1[0]とintlist2[0]は別のオブジェクトを参照するようになる。例えば、「instlist1[0] = 'foo'」としたら次のようになる。
だが、intlist[0]が参照しているリストの内容を変更することは、つまりintlist[0][0]などへの代入を行うことは、intlist2[0][0]の内容を変更することに他ならない。「intlist[0][0] = 101」とすれば次のようになるということだ。
intlist1[0][0]の変更の影響がintlist2にまで及んだのはこうした理由があるからだ。
これに対して、参照をコピーするのではなく、参照をたどりながら最後の要素までコピーする方法のことを「深いコピー」と呼ぶ。ただし、こちらは処理に時間がかかるので、プログラミング言語でリストのような多数の値を格納するデータ構造をコピーするときには一般的に「浅いコピー」が行われるようになっている。Pythonで「深いコピー」を行うには、copyモジュールが提供するdeepcopy関数を利用できる。詳しくは説明しないがサンプルコードを示しておこう。
from copy import deepcopy
intlist1 = [[1, 2], [3, 4], [5, 6]]
intlist2 = deepcopy(intlist1) # deepcopy関数で「深いコピー」を行う
print(intlist1)
print(intlist2)
intlist1[0][0] = 101
print(intlist1) # この結果と
print(intlist2) # この結果は異なる
実行結果を以下に示す。
ご覧の通り、先ほどとは異なり、deepcopy関数で「深いコピー」を作成した場合には、intlist1[0][0]の変更がintlist2に影響を及ぼしていないことが分かる。
今回はPythonのリストを操作するさまざまなメソッドや関数、文を見てきた。次回はリストと繰り返し処理にフォーカスを当て、繰り返し処理の基本や、繰り返し処理で便利に使える関数、繰り返し処理を行わずに同等な処理を行う方法などについて見ていく。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.