1
4

More than 1 year has passed since last update.

変数の完全コピーにおける代入・copy・copyメソッド・deepcopyの使い分け表

Last updated at Posted at 2023-04-22

はじめに

 コピーには浅いコピーと深いコピーがあって…という記事はありますが、「どう使い分ければ良いの?」という観点の情報がないので表形式でまとめました。

本記事におけるコピーの目的

 本記事ではオリジナルの変数値を書き換わらない形で別の変数に完全にコピーすることを目的とします。そのため部分的にコピーしたい場合については目的外とします。

オリジナルの値が書き換わる例と書き換わらない例
import copy
# オリジナルの値を用意
a = [0, 1, 2]

# 2つの変数にオリジナルの値をコピー(?)する
a_copy = copy.copy(a)  # copyを使う
a_assignment = a  # 代入する

# オリジナルの値を書き換える
a[0] = 1

# コピー(?)した値を確認する
print(a_copy)  # [0, 1, 2]値が書き換わっていない!目的通りの動き!
print(a_assignment)  # [1, 1, 2]値が書き換わっている…目的の動きになっていない…

本記事で扱わないこと

  • イミュータブルやミュータブルの詳しい説明は他の方の記事が詳しいため、この記事では扱いません。
  • 詳しい動作まで説明しません。各手段の使い分けがこの記事の目的です。詳しく知りたい方はこちらを参照してください。
  • 上記の理由から組み込み関数id()を使った確認コードは示しません。

使い分け表

 コピーの目的を達成したいときは以下のよう代入、copy、copyメソッド、deepcopyを使い分けると良いです。△については後述します。

表で使われる言葉の定義
import copy
a = [0, 1, 2]  # オリジナルの値

# 表で使われる言葉は以下の処理を意味します
a_assignment = a  # 代入
a_copy = copy.copy(a)  # copy
a_copy_method = a.copy()  # copyメソッド
a_deepcopy = copy.deepcopy(a)  # deepcopy
オブジェクトの種類 型の例  具体例   代入  copy copyメソッド   deepcopy
イミュータブル int, strなど 1 -
ミュータブル list, dictなど [0, 1] ×
ミュータブル(複合オブジェクト) 入れ子構造のlist, dictなど [0, 1, [2, 3]] × × ×

○…目的の動きをする最小のコード
△…目的の動きをする
×…目的の動きにならない
-…使用できない

△について

イミュータブルのcopyとdeepcopy

 イミュータブルなオブジェクトについてはcopydeepcopyを使わなくても目的の動きができます。コード量が増えるので特別な意図がない限りは代入で十分です。

ミュータブルのdeepcopy

 deepcopyでは入れ子構造まで再帰的にコピーを行います。入れ子構造になっていないlist等であれば特別な意図がない限りはcopyで十分です。コピー対象が複合オブジェクトに変更となることを考慮する場合はdeepcopyで統一した方が良いです。

ミュータブル(複合オブジェクト)の例

 言葉だけだと分かりにくいので例を示します。deepcopyのときだけオリジナルの値が保存されていることが分かります。

import copy

# 例1
l = [0, 1, [2, 3]]
l_copy = copy.copy(l)
l_copy_method = l.copy()
l_deepcopy = copy.deepcopy(l)
l[2][0] = 9
print(l)  # [0, 1, [9, 3]]
print(l_copy)  # [0, 1, [9, 3]]
print(l_copy_method)  # [0, 1, [9, 3]]
print(l_deepcopy)  # [0, 1, [2, 3]]

# 例2
l = [{"a": 0, "b": 1}, {"a": 2, "b": 3}]
l_copy = copy.copy(l)
l_copy_method = l.copy()
l_deepcopy = copy.deepcopy(l)
l[0]["a"] = 9
print(l)  # [{'a': 9, 'b': 1}, {'a': 2, 'b': 3}]
print(l_copy)  # [{'a': 9, 'b': 1}, {'a': 2, 'b': 3}]
print(l_copy_method)  # [{'a': 9, 'b': 1}, {'a': 2, 'b': 3}]
print(l_deepcopy)  # [{'a': 0, 'b': 1}, {'a': 2, 'b': 3}]

# 例3
d = {"a": {"aa": 0, "bb": 1}, "b": 2}
d_copy = copy.copy(d)
d_copy_method = d.copy()
d_deepcopy = copy.deepcopy(d)
d["a"]["aa"] = 9
print(d)  # {'a': {'aa': 9, 'bb': 1}, 'b': 2}
print(d_copy)  # {'a': {'aa': 9, 'bb': 1}, 'b': 2}
print(d_copy_method)  # {'a': {'aa': 9, 'bb': 1}, 'b': 2}
print(d_deepcopy)  # {'a': {'aa': 0, 'bb': 1}, 'b': 2}

# 例4
d = {"a": [0, 1], "b": 2}
d_copy = copy.copy(d)
d_copy_method = d.copy()
d_deepcopy = copy.deepcopy(d)
d["a"][0] = 9
print(d)  # {'a': [9, 1], 'b': 2}
print(d_copy)  # {'a': [9, 1], 'b': 2}
print(d_copy_method)  # {'a': [9, 1], 'b': 2}
print(d_deepcopy)  # {'a': [0, 1], 'b': 2}
1
4
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4