概要
Python
では、オブジェクトをコピーする際に「浅いコピー」と「深いコピー」という2つの異なる方法があります。この2つのコピー方法は、特にリストや辞書などのミュータブル(変更可能)なオブジェクトを扱うときに重要です。本記事では、浅いコピーと深いコピーの違いを理解し、Python
でどのようにそれらを適切に使い分けるかを解説します。
浅いコピー(shallow copy)とは
浅いコピーとは、元のオブジェクトのトップレベルの構造のみをコピーし、ネストされたオブジェクト(例えばリスト内のリストや辞書内のリストなど)はコピーせず、元のオブジェクトと同じ参照を持つものです。つまり、浅いコピーではネストされたオブジェクトが元のオブジェクトと共有されるため、コピー後にネストされたオブジェクトを変更すると、元のオブジェクトにも影響を与える可能性があります。
Python
で浅いコピーを作成するには、組み込みのcopy
モジュールのcopy()
関数を使用します。
浅いコピーの例
import copy
# 元のリスト
original = [[1, 2, 3], [4, 5, 6]]
# 浅いコピーを作成
shallow_copy = copy.copy(original)
# トップレベルのリストは異なるが、内部のリストは同じ参照を持つ
print(original is shallow_copy) # False(別のオブジェクト)
print(original[0] is shallow_copy[0]) # True(同じ内部リストを参照)
# 浅いコピーの内部リストを変更すると、元のリストにも影響が出る
shallow_copy[0][0] = 100
print(original) # 出力: [[100, 2, 3], [4, 5, 6]]
この例では、トップレベルのリストは新しく作成されていますが、その中に含まれるリストは元のオブジェクトと参照を共有しているため、shallow_copy
を変更するとoriginal
にも影響を与えています。
深いコピー(deep copy)とは
深いコピーは、オブジェクトそのものだけでなく、その中に含まれるすべてのオブジェクトも再帰的にコピーする方法です。深いコピーを使用すると、元のオブジェクトとコピーしたオブジェクトは完全に独立した存在になります。これにより、コピー後にいずれかのオブジェクトを変更しても、もう一方には一切影響を与えません。
深いコピーはcopy.deepcopy()
を使用して作成します。
深いコピーの例
import copy
# 元のリスト
original = [[1, 2, 3], [4, 5, 6]]
# 深いコピーを作成
deep_copy = copy.deepcopy(original)
# トップレベルのリストも、内部のリストも異なるオブジェクト
print(original is deep_copy) # False(別のオブジェクト)
print(original[0] is deep_copy[0]) # False(内部リストも異なるオブジェクト)
# 深いコピーの内部リストを変更しても、元のリストには影響がない
deep_copy[0][0] = 100
print(original) # 出力: [[1, 2, 3], [4, 5, 6]]
この例では、deep_copy
は元のリストと完全に独立しているため、deep_copy
を変更してもoriginal
には一切影響がありません。
浅いコピーと深いコピーの違い
浅いコピーと深いコピーの最大の違いは、ネストされたオブジェクトがどのように扱われるかです。
- 浅いコピー:トップレベルのオブジェクトのみをコピーし、ネストされたオブジェクトは元のオブジェクトと参照を共有する。
- 深いコピー:オブジェクト全体を再帰的にコピーし、元のオブジェクトと完全に独立した新しいオブジェクトを作成する。 これにより、浅いコピーではネストされたオブジェクトを変更すると元のオブジェクトにも影響が出ますが、深いコピーではそのような影響はありません。
比較まとめ
種類 | トップレベルのコピー | ネストされたオブジェクトのコピー |
---|---|---|
浅いコピー | コピーされる | 参照を共有する |
深いコピー | コピーされる | 再帰的にコピーされる |
使い分けのポイント
- 浅いコピーを使う場面
浅いコピーは、元のオブジェクトの一部だけを変更したい場合や、ネストされたオブジェクトが変更されることを許容する場合に使用します。例えば、ネストされたオブジェクトの変更が望ましいシナリオでは、浅いコピーが有効です。 - 深いコピーを使う場面
一方、元のオブジェクトとコピーされたオブジェクトが完全に独立している必要がある場合には、深いコピーを選択します。これは、ネストされたオブジェクトを含む複雑なデータ構造を扱う際に特に重要です。例えば、設定データやクラスインスタンスの完全な複製が必要な場合などです。
注意点
パフォーマンスの問題
深いコピーは、浅いコピーに比べて処理が重くなる可能性があります。これは、再帰的にオブジェクトをコピーするため、ネストされたオブジェクトが多いほど時間がかかるからです。特に、非常に複雑なデータ構造を持つ場合、深いコピーは注意して使う必要があります。
イミュータブルなオブジェクト
Python
の文字列やタプルなどのイミュータブル(変更不可能)なオブジェクトは、浅いコピーも深いコピーも同じ結果を生むことが多いです。これは、イミュータブルなオブジェクトは変更されることがないため、参照を共有しても安全だからです。
import copy
# イミュータブルなオブジェクトのコピー
original = (1, 2, 3)
shallow_copy = copy.copy(original)
deep_copy = copy.deepcopy(original)
# どちらも同じオブジェクトを参照
print(original is shallow_copy) # True
print(original is deep_copy) # True
この例では、タプル(イミュータブル)の場合、浅いコピーも深いコピーも同じ参照を持つため、同じオブジェクトを指しています。
結論
Python
でオブジェクトをコピーする際、浅いコピーと深いコピーの違いを理解することは非常に重要です。特に、複雑なデータ構
造を扱う場合、この2つのコピー方法を適切に使い分けることで、意図しない副作用を避けることができます。浅いコピーは軽量で一部の用途に適していますが、独立したコピーが必要な場合は深いコピーを使用するべきです。