ときたまpythonを書いているとこれって値渡しなんだっけ、参照渡しなんだっけと分からなくなります。 たとえば

hoge = {1:1, 2:2, 3:3}
print id(hoge) # 140418104920496

fuga = hoge
print id(fuga) # 140418104920496

fuga.update({
    4:4
})

print hoge == fuga # True

Pythonistなら常識ですよね。

どうすれば回避できるのか

割と単純です。

hoge = {1:1, 2:2, 3:3}
print id(hoge) # 140418104920496

fuga = hoge.copy()
print id(fuga) # 140418107009728

fuga.update({
    4:4
})

print hoge == fuga # False

print hoge # {1:1, 2:2, 3:3}

print fuga # {1:1, 2:2, 3:3, 4:4}

辞書の浅いコピーを取るにはcopy_dict = dict.copy()が簡単ですね。

ちなみに、リストの場合はcopy_list = original_list[:]でコピーが取れます。

ただこれだと浅いコピーになります。

なので

hoge = {1:{2:2, 3:3}}
print id(hoge) # 140418104920496

fuga = hoge.copy()
print id(fuga) # 140418107009728

fuga[1].update({
    4:4
})

print hoge == fuga # True

print hoge # {1: {2: 2, 3: 3, 4: 4}}

print fuga # {1: {2: 2, 3: 3, 4: 4}}

となってしまいます。

浅いコピーの場合は

  • 元のオブジェクト中に見つかったオブジェクトに対する 参照 を挿入

するようです。

深いコピー

上記の通り、オブジェクトの中のオブジェクトは参照渡しになります。

それを回避するために、copyモジュールをimportして、深いコピーを利用します。

import copy

hoge = {1:{2:2, 3:3}}
print id(hoge) # 140418104920496

fuga = copy.deepcopy(hoge)
print id(fuga) # 140418107009728

fuga[1].update({
    4:4
})

print hoge == fuga # False

print hoge # {1: {2: 2, 3: 3}}

print fuga # {1: {2: 2, 3: 3, 4: 4}}

あら素敵。

結構厄介な場面も多いのできちんと覚えたいものです。

参考

8.17. copy — 浅いコピーおよび深いコピー操作 — Python 2.6ja2 documentation