【Python】copyの仕様解説(deepcopy と shallowcopy)

【Python】copyの仕様解説(deepcopy と shallowcopy)Python
ゆうすけ
ゆうすけ

Pythonを勉強しているんですが、

shallow copyとdeep copyの違いが分かりません。

資格マフィア
資格マフィア

shallow copyとdeep copyは実は大きく違う。
その違いはcodeで押さえておくと分かりやすいぞ。

 

✔️ 本記事のテーマ

Pythonにおける、shallow copyとdeep copyの違い

 

✔️ 読者さんへの前置きメッセージ

結論から述べると、以下の通りです。

shallow copyとdeep copyの違いは
オブジェクト内のオブジェクトの参照先のアドレスの違い

 

本記事は「shallow copyとdeep copyの違い」について書いています。

 

この記事を読むことで
「Pythonにおける、shallow copyとdeep copy」
について理解できます。

 

Pythonでは浅いcopy(shallow copy)と深いcopy(deep copy)によって挙動が違います。

 

この違いを正しく理解していないと、思わぬバグを生んでしまったり、
プログラムの動きが分からないことがあるでしょう。

 

本記事では、
浅いcopy(shallow copy)と深いcopy(deep copy)の違いを
sample codeとともに解説します。

 

copyライブラリによるshallow copyとdeep copy

copyライブラリによるshallow copyとdeep copy

Pythonには標準ライブラリとしてcopyライブラリが用意されています。

 

このcopyライブラリにはcopy.copy()とcopy.deepcopy()が用意されています。

 

copy.copy()がshallow copyで、copy.deepcopy()がdeep copyです。

 

この2つのメソッドの違いを説明しましょう。

 

要点だけ説明すると、

  • shallow copy も deep copycopy も oirigin とは別のメモリを参照している
  • oirigin の要素オブジェクトが変更された場合、shallow copy も deep copy も影響を受けない
  • oirigin の要素オブジェクト内のオブジェクトが変更された場合、shallow copy は影響を受けるが、deep copy は影響を受けない

となります。

 

最も重要なポイントは3点目です。codeで説明しましょう。

 

sample code(shallow copyとdeep copyの違い)

sample code(shallow copyとdeep copyの違い)

shallow copyとdeep copyの違いをcodeで示すと以下のようになります。

 

import copy


one = [1, 1]
two = [2, 2]
three = [3, 3]

origin = [one, two, three]        # オリジナル
shallow_copy = copy.copy(origin)  # 浅いcopy
deep_copy = copy.deepcopy(origin) # 深いcopy

# originの要素オブジェクトを変更する
origin[0] = -1
# originの要素オブジェクト内のオブジェクトを変更する
origin[1][0] = 99


print('origin  = ', origin)
print('shallow = ', shallow_copy)
print('deep    = ', deep_copy)

# origin  = [-1, [99, 2], [3, 3]]
# shallow = [[1, 1], [99, 2], [3, 3]]
# deep    = [[1, 1], [2, 2], [3, 3]]

 

最後のprint出力結果が全てですが、順を追って解説していきましょう。

 

オリジナルの要素オブジェクトが変更された場合、shallow copy も deep copy も影響を受けない

オリジナルの要素オブジェクト(=origin[0])が変更されたとき、
shallow copy(=shallow_copy) 、deep copy(=deep_copy) はどちらもその影響を受けません。
これをsample codeから抜き出して解説しましょう。

 

# originの要素オブジェクトを変更する
origin[0] = -1

 

ここで、originの要素0番目のオブジェクトを -1 に書き換えています。
このとき、shallow_copyもdeep_copyもこの変更の影響を受けていません。
どちらのlistも要素0番目には [1, 1] が格納されています。

# shallow = [[1, 1], [99, 2], [3, 3]]
# deep    = [[1, 1], [2, 2], [3, 3]]

oirigin の要素オブジェクト内のオブジェクトが変更された場合、shallow copy は影響を受けるが、deep copy は影響を受けない

一方、オリジナルの要素オブジェクト内のオブジェクト(=origin[1][0])が変更されたとき、
shallow copy(=shallow_copy) はその影響を受けますが、
deep copy(=deep_copy) はその影響を受けません。

 

ここがshallow copy と deep copy の大きな違いです。
sample codeから抜き出して解説しましょう。

 

ここで、originの要素1番目のオブジェクト内のオブジェクトを 99 に書き換えています。

# originの要素オブジェクト内のオブジェクトを変更する
origin[1][0] = 99

 

このとき、shallow_copyとdeep_copyで挙動が変わります。
shallow_copyはoriginの変更の影響を受けますが、
一方、deep_copyはoriginの変更の影響を受けません。

 

shallow_copyの要素1番目には [99, 2] が格納されていますが、
deep_copyの要素1番目には [2, 2] が格納されています。

# shallow = [[1, 1], [99, 2], [3, 3]]
# deep    = [[1, 1], [2, 2], [3, 3]]

なぜこのようなことが起こるか(copyの仕様)

なぜこのようなことが起こるか

このようなことが起こるのは、listに対してcopyを使用した時の参照先の違いに起因します。

  • shallow copy: list自体はoriginと別のものを参照、list内の要素はoriginと同じものを参照
  • deep copy : list自体はoriginと別のものを参照、list内の要素もoriginと別のものを参照

 

shallow copyとdeep copyの参照を図で表すと、以下のようになっています。

shallowcopyとdeepcopyの違い

試しに、id()を使って、参照先を出力してみましょう。

print(id(origin), id(origin[0]))
print(id(shallow_copy), id(shallow_copy[0]))
print(id(deep_copy), id(deep_copy[0]))

# 4506466368 4506397312
# 4506552704 4506397312
# 4506552128 4506552768

このように、id(origin),id(shallow_copy),id(deep_copy)は全て別のIDを示しています。

 

一方、id(origin[0]),id(shallow_copy[0])は同じIDですが、id(deep_copy[0])は別のIDを示しています。

 

この「id(origin[0]),id(shallow_copy[0])は同じID」という部分が重要で、これにより、originのlist内の要素を変更したとき、連動してshallow copyの中身も変更されるのです。

 

一言でまとめると

一言でまとめると

shallow copyはオリジナルの2階層目以降を代入するのに対して、
deep copyはオリジナルの全階層を再帰的に複製する。

 

※ 注
なお、Pythonのcopyの仕様について厳密に解説するなら、mutable object / immutable object について言及する必要があります。
ただ、そこまで広げてしまうと話が大きく、そして複雑になるので、本記事ではmutable objectを前提として解説しています。
(sample codeでもlist(=mutable object)を使って説明しています)

Pythonの言語仕様についてもっとスキルをつけるなら

Pythonの言語仕様についてもっとスキルをつけるなら

Pythonのcopyの仕様について解説しました。

 

Pythonをマスターすると様々なことがプログラムでできるようになります。

Pythonを深く網羅的に学ぶためには以下の書籍がオススメです。

 

この書籍はPythonについてかなり深く、そして丁寧に書かれています。

Python の基本文法だけでなく、

いかにして Python の持つ力を最大限引き出せるかという視点で書かれています。

 

Python 初心者はもちろん、すでに Python を使える方にもオススメの一冊です。

エンジニアとしての自身の価値をチェックする(完全無料)

エンジニアとして、

自分の価値がどれくらいのものかご存知でしょうか?

 

エンジニアとしてIT業界に身を置いていると

今の会社でずっと働くのか、フリーランスとして独立するのか …

と様々な選択肢があります。

 

どの選択肢が正解なのかを見極めるためにも、選択肢を広げるためにも

自身の価値を知っておくことはとても重要です。

 

TechClips ME では、

職務経歴書をアップロードするだけで企業からのスカウトを受けることができます。

▼▼▼▼▼

▲▲▲▲▲

しかもTechClips MEでは想定年収を企業から提示してくれるので、

自身の価値を数字で分かりやすくたしかめることができます。

 

登録はもちろん完全無料なので、一度登録してみると良いかもしれません。

 

コメント

タイトルとURLをコピーしました