Neunomizuの日記

俺だけが俺だけじゃない

# 留年しても分かるPythonのshallow copyとdeep copy

tags: 情報

shallow copydeep copyの違いとか曖昧だけど必要になったら毎回ググればえーやろ^^

という精神で生きていたらググれない状況でそれを聞かれて破滅しました.こんなのも分からず生きていてごめんなさい...と思いましたが,他人がこれを知らなくても許せる優しい人間になりたい.

人にやさしく自分に厳しい人間になるのが夢です

...俺を反面教師にして「ググったらえーだけやろ」という考えは捨てろ!今すぐだ!

そもそもPythonの代入文は

Pythonの代入文ではmutableなものはアドレスをコピーしているだけなので

# coding: utf-8

b = a = 123
b += 456
print("a = {}, b = {}".format(a, b))

d = c = [1, 2, 3]
d.append(4)
print("c = {}, d = {}".format(c, d))

f = e = "ariga10"
f += "10"
print("e = {}, f = {}".format(e, f))

と書くと出力は

a = 123, b = 579
c = [1, 2, 3, 4], d = [1, 2, 3, 4]
e = ariga10, f = ariga1010

となります.リストはmutableなのでアドレスのコピーが変数に代入されて,それ以外はimmutableなオブジェクトなので値が代入されるというわけです.

mutableなものとしてはlist, set, dictが挙げられます.

Copy

copyというモジュールにあるcopy()というメソッドを使うことで新しいオブジェクトを作り,この問題を解消できるのです.

しかし,ここにも問題があります.copyにはshallow copydeep copyという違いがあることです.

ふざけるなよ...?なんで2つあるんだよ^^;

ちなみに,組み込みメソッドのcopy()shallow copy?だからPythonは嫌いなんだよ^^;

まあ落ち着けよ俺^^;

挙動

下のようなコードを書きます.

# coding: utf-8
import copy

a = [1, 2, [3, 4]]
b = copy.copy(a)
b[0] = 29
b[2][0] = 4649
print("a = {}, b = {}".format(a, b))

c = [5, 6, [7, 8]]
d = copy.deepcopy(c)
d[0] = 810
d[2][0] = 114514
print("c = {}, d = {}".format(c, d))

これを実行すると以下のような結果を得ます.

a = [1, 2, [4649, 4]], b = [29, 2, [4649, 4]]
c = [5, 6, [7, 8]], d = [810, 6, [114514, 8]]

copy.copy()shallow copy,つまり,mutableなオブジェクトの中のmutableなオブジェクトに関してはアドレスしか返さないです.

一方,copy.deepcopy()deep copy,つまりmutableなオブジェクトの中のmutableなオブジェクトに関しても同じオブジェクトを作成して返します.

このコードでは変数a内のリストを参照した値がbのリストに入っているので,bのリストの要素が変更されるとaにまで影響が及びます.

図示すると下のようになります.

まとめ

  • Pythonではmutableな値とimmutableな値のcopyには細心の注意を払おう
  • ググればええやんは面接では通じないし,ググらなくてもいいようにした方がいいよ(N=1)
  • 留年しそうになると頭がやられます