Python に限らず、変数をコピーする際には様々な深さのコピー概念があります。
この記事では、リストに対する次の3パターンのコピーについて違いを解説します。
- 参照コピー
- シャローコピー(浅いコピー)
- ディープコピー(深いコピー)
各コピーの概要
まず各コピーの概要をまとめます。
参照コピー | リストの参照のみ複製する。 リストは再作成されない。 |
シャローコピー | リストを再作成する。 リスト内の各要素は参照コピーとなる。 |
ディープコピー | リストの末端要素まで再帰的に全て複製する。 |
参照コピーとシャローコピーの違い
参照コピーの例
次のようなコードを考えます。
これは「参照コピー」の例です。
a = [1, 2, 3]
b = a # ここで参照コピー
b[0] = 5
print(a[0])
# 5 <- a のリストが更新されている
ここでは、リストを a から b へコピーしていますが、その後に b[0] を 5 に更新しています。
参照コピーの場合は、この操作によって a のリストも同時に更新されることになります。
リストを複製せず、a と b で同じリストを共有しているためです。
シャローコピーの例
同様のケースで、シャローコピーの例を示します。
a = [1, 2, 3]
b = a.copy() # ここでシャローコピー
b[0] = 5
print(a[0])
# 1 <- a のリストには影響しない
シャローコピーでは、b へコピーする際にリストを作り直します。
その結果、b[0] を更新したとしても a のリストには影響を及ぼしません。
シャローコピーとディープコピーの違い
a = [1, 2, 3]
の例においては、シャローコピーとディープコピーの挙動に違いはありません。
なぜならば、int 型の変数に対しては参照コピーの概念がなく、常に値の複製が行われるためです。
このリストに対してシャローコピーの操作を行うと、結果的に int 型の要素まで複製されます。
リストの中に、クラスインスタンスなどのように参照コピーがされる要素が含まれると、シャローコピーとディープコピーに違いがでてきます。
シャローコピーの例
少しだけ複雑になりますが、シャローコピーの例を示します。
class Value:
def __init__(self, value):
self.value = value
a = [1, 2, Value(3)]
b = a.copy() # ここでシャローコピー
b[2].value = 5
print(a[2].value)
# 5 <- コピーしたにも関わらず、a 要素が更新される
リスト a の第3要素を、クラスAのインスタンスに変更しています。
リスト a を b にシャローコピーしたうえで、b[2].value の値を更新しています。
シャローコピーにおいては、Value(3) のインスタンスは参照コピーされるため、a と b で同一のインスタンスを共有することになります。
ディープコピーの例
次にディープコピーの例です。
from copy import deepcopy
class Value:
def __init__(self, value):
self.value = value
a = [1, 2, Value(3)]
b = deepcopy(a) # ここでディープコピー
b[2].value = 5
print(a[2].value)
# 3 <- b を更新しても a には影響しない
こちらの例では、a を末端まで完全複製するため、b を更新しても a の要素には影響が発生しません。
コーディングごとのコピーパターン
どういったコードを記述すると、どういったコピーが行われるかというのを示します。
参照コピー
基本的には次のコードのように、単純な代入をするケースが、リストの参照コピーとなります。
a = [1, 2, 3]
b = a # 参照コピー
b[0] = 5
print(a[0])
# 5
その他の例では、関数の引数にリストを渡す際も参照コピーとなります。
a = [1, 2, 3]
def f(b):
b[0] = 5
f(a) # a を引数 b に参照コピー
print(a[0])
# 5
シャローコピー
a に対して何かしらの操作を行うと、大抵のケースはシャローコピーになります。
以下の例は全てシャローコピーになります。
from copy import copy
a = [1, 2, 3]
b = list(a)
b = [*a]
b = a + []
b = a.copy()
b = copy(a)
リストが再作成され、中の要素は参照コピーとなります。
ただし、上記の例では要素が全て int 型のため、結果的に要素に対しても値コピーが行われます。
ディープコピー
前述の deepcopy 関数を利用するケースが、ディープコピーの例となります。
from copy import deepcopy
a = [1, 2, 3]
b = deepcopy(a)
基本的には明示的に意識しない限りは、ディープコピーが行われることはありません。
コメント