home changes contents help options

084:配列を複製する

Python は複数要素の入れ物となる組み込み型が豊富です。 シーケンス型 であるリストとタプル、 マップ型 である辞書、 集合型 である set, frozenset 。 これらを複製するときの話になります。

要素が不変オブジェクトのみの場合

リスト、辞書、 set を複製するにはそれぞれ 複製対象を引数に list, dict, set 関数を呼ぶのがわかりやすいです。:

   >>> a = [0, 1, 2, 3]
   >>> b = list(a)
   >>> c = a # 単なる代入ではダメです
   >>> a
   [0, 1, 2, 3]
   >>> b
   [0, 1, 2, 3]
   >>> c
   [0, 1, 2, 3]
   >>> a[0] = 999
   >>> a
   [999, 1, 2, 3]
   >>> b
   [0, 1, 2, 3]
   >>> c
   [999, 1, 2, 3]

copy.copy 関数を使うことでも複製を作れます。 また、リストは [:]? スライスを得る方法でも複製を作れます。 set は copy メソッドを持ちこれを呼ぶことでも複製を作れます。

タプル、 frozenset は不変ですので複製する必要がありません。 よって単に代入してしまって問題ありません。:

   >>> a = (0, 1, 2)
   >>> b = a
   >>> a[0] = 999
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'tuple' object does not support item assignment

これらの要素が指し示す先が 変化する心配はありません。

要素に可変オブジェクトを含む場合

上記の手法らは指しているもの自体が変化するのを 避けることができません。次はリストを 2 つ含むタプルを上記手法で複製した様子です。:

   >>> a = range(3)
   >>> b = range(3,6)
   >>> c = (a, b)
   >>> d = c
   >>> c
   ([0, 1, 2], [3, 4, 5])
   >>> d
   ([0, 1, 2], [3, 4, 5])
   >>> a[0] = "a"
   >>> c
   (['a', 1, 2], [3, 4, 5])
   >>> d
   (['a', 1, 2], [3, 4, 5])

この動作では都合が悪く、完全な複製を作りたいときには copy.deepcopy 関数を使います。:

   >>> import copy
   >>> e = copy.deepcopy(d)
   >>> b[0] = "b"
   >>> d
   (['a', 1, 2], ['b', 4, 5])
   >>> e
   (['a', 1, 2], [3, 4, 5])

完全な複製を作ろうとするまえに本当に必要かどうかを考え直してみてください。 そして、対象となるオブジェクトの大きさに注意してください。 複雑に入れ子になった巨大シーケンスの copy.deepcopy などは重たい処理の部類に入ります。