連載
» 2019年06月04日 05時00分 公開

Python入門:[Python入門]リストの操作 (4/4)

[かわさきしんじ,Deep Insider編集部]
前のページへ 1|2|3|4       

リストのコピー:copyメソッド

 あるリストの内容をコピーするには、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))

copyメソッドの使用例

 実行結果を以下に示す。

実行結果 実行結果

 実行結果を見ると、変数strlist1とstrlist3を指定してid関数を呼び出した結果は「139717167265800」となっているので、これらは同じリストを参照していることが分かる(「strlist3 = strlist1」としているので当然だ)。一方、変数strlist2にはcopyメソッドで作成したリストを代入したので、これを指定してid関数を呼び出した結果は「139717167103816」と、元のリストとは別物になっている。

3つの変数が参照しているもの 3つの変数が参照しているもの

 変数strlist1とstrlist3は同じリストを参照しているので、どちらかの要素を変更すれば、もう一方の要素も変更される。

strlist1[0] = 'FOO'
print(strlist3)  # ['FOO', 'bar', 'baz']

strlit1の要素を変更すると、strlist3の要素も変更される

 実行結果を以下に示す。

実行結果 実行結果

 strlist1の要素を変更したのに、print関数でstrlist3の内容を表示すると、その要素が変わっていることに注目しよう。

 これに対して、例えばstrlist2[0]を変更しても、それは「strlist[0]という名札を付け替える」ことなので、strlist1やstrlist3には変更は及ばない。

strlist2[0] = 'Foo'
print(strlist1)
print(strlist2)

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が参照するリストの内容は同じだが、それらのアイデンティテイーは異なるものとなる。

 次に、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とintlist2は別のものだが、内部では同じオブジェクトを参照している intlist1とintlist2は別のものだが、内部では同じオブジェクトを参照している

 ここでintlist1[0]へ代入をすれば、名札の付け替えが行われるので、intlist1[0]とintlist2[0]は別のオブジェクトを参照するようになる。例えば、「instlist1[0] = 'foo'」としたら次のようになる。

intlist1の要素を書き換えると、intlist2の要素とは別のものを参照するようになる intlist1の要素を書き換えると、intlist2の要素とは別のものを参照するようになる

 だが、intlist[0]が参照しているリストの内容を変更することは、つまりintlist[0][0]などへの代入を行うことは、intlist2[0][0]の内容を変更することに他ならない。「intlist[0][0] = 101」とすれば次のようになるということだ。

intlist1の要素であるリストの要素を変更すると、その影響はintlist2にも及ぶ intlist1の要素であるリストの要素を変更すると、その影響はintlist2にも及ぶ

 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のリストを操作するさまざまなメソッドや関数、文を見てきた。次回はリストと繰り返し処理にフォーカスを当て、繰り返し処理の基本や、繰り返し処理で便利に使える関数、繰り返し処理を行わずに同等な処理を行う方法などについて見ていく。

今回のまとめ:リストの操作

  • リストの要素数を求めるにはlen関数を使用する
  • リストに含まれる最大/最小の要素を取得するにはmax関数/min関数を使用する
  • リストは「+」演算子で結合したり、「*」演算子で乗算したりできる
  • リストに特定の要素が含まれているかを確認するにはin演算子/not in演算子を使用する
  • 要素が格納されているインデックスを取得するにはindexメソッドを使用する
  • 指定した要素が何個リストに格納されているのかを数えるにはcountメソッドを使用する
  • リストへ要素を追加するにはappendメソッド/extendメソッドを使用する
  • appendメソッドの引数にリストを渡すと、それがそのままリストに追加される
  • extendメソッドの引数にリストを渡すと、その要素が展開されてリストに追加される
  • リストへ要素を挿入するにはinsertメソッドを使用する
  • リストから要素を削除するにはdel文/removeメソッド/popメソッド/clearメソッドを使用する
  • del文は「del リスト[インデックス]」として、指定したインデックスにある要素を削除する
  • removeメソッドは、引数に指定した値を持つ要素の中で、インデックスが最小のものを削除する
  • popメソッドは、引数に指定したインデックスにある要素を削除して、その値を戻り値とする。インデックスを指定しなかった場合は末尾の要素を削除して、その値を戻り値とする
  • clearメソッドはそのリストの全要素を削除する
  • リストを並べ替えるにはsortメソッド/sorted関数を使用する。前者はリスト自体を書き換え、後者はリストを引数に受け取り、並べ替えた新しいリストを戻り値とする(元のリストは以前のまま)
  • リストを反転するにはreverseメソッド/reversed関数を使用する。前者はリスト自体を書き換え、後者はリストの要素を逆順に取り出すイテレータを戻り値とする。イテレータをlist関数に渡すことで、リストが得られる
  • リストをコピーするにはcopyメソッドを使用する。このときには「浅いコピー」が行われる

「Python入門」のインデックス

Python入門

前のページへ 1|2|3|4       

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。