Python | リストの参照コピー・シャローコピー・ディープコピー

Python に限らず、変数をコピーする際には様々な深さのコピー概念があります。
この記事では、リストに対する次の3パターンのコピーについて違いを解説します。

  • 参照コピー
  • シャローコピー(浅いコピー)
  • ディープコピー(深いコピー)
目次

各コピーの概要

まず各コピーの概要をまとめます。

参照コピーリストの参照のみ複製する。
リストは再作成されない。
シャローコピーリストを再作成する。
リスト内の各要素は参照コピーとなる。
ディープコピーリストの末端要素まで再帰的に全て複製する。

参照コピーとシャローコピーの違い

参照コピーの例

次のようなコードを考えます。
これは「参照コピー」の例です。

Python
a = [1, 2, 3]

b = a  # ここで参照コピー

b[0] = 5
print(a[0])
# 5    <- a のリストが更新されている

ここでは、リストを a から b へコピーしていますが、その後に b[0] を 5 に更新しています。
参照コピーの場合は、この操作によって a のリストも同時に更新されることになります。
リストを複製せず、a と b で同じリストを共有しているためです。

シャローコピーの例

同様のケースで、シャローコピーの例を示します。

Python
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 型の要素まで複製されます。

リストの中に、クラスインスタンスなどのように参照コピーがされる要素が含まれると、シャローコピーとディープコピーに違いがでてきます。

シャローコピーの例

少しだけ複雑になりますが、シャローコピーの例を示します。

Python
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 で同一のインスタンスを共有することになります。

ディープコピーの例

次にディープコピーの例です。

Python
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 の要素には影響が発生しません。

コーディングごとのコピーパターン

どういったコードを記述すると、どういったコピーが行われるかというのを示します。

参照コピー

基本的には次のコードのように、単純な代入をするケースが、リストの参照コピーとなります。

Python
a = [1, 2, 3]
b = a  # 参照コピー

b[0] = 5
print(a[0])
# 5

その他の例では、関数の引数にリストを渡す際も参照コピーとなります。

Python
a = [1, 2, 3]

def f(b):
    b[0] = 5
    
f(a)  # a を引数 b に参照コピー

print(a[0])
# 5

シャローコピー

a に対して何かしらの操作を行うと、大抵のケースはシャローコピーになります。
以下の例は全てシャローコピーになります。

Python
from copy import copy

a = [1, 2, 3]

b = list(a)
b = [*a]
b = a + []
b = a.copy()
b = copy(a)

リストが再作成され、中の要素は参照コピーとなります。
ただし、上記の例では要素が全て int 型のため、結果的に要素に対しても値コピーが行われます。

ディープコピー

前述の deepcopy 関数を利用するケースが、ディープコピーの例となります。

Python
from copy import deepcopy

a = [1, 2, 3]

b = deepcopy(a)

基本的には明示的に意識しない限りは、ディープコピーが行われることはありません。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次