Python の deepcopy

…は、(古典的な)deep copy じゃない。というを、某 Python スレにて。

import copy
a0 = [[1, 2, 3]] * 3
a1 = copy.deepcopy(a0)
a0[0][0] = 'hoge'
a1[0][0] = 'fuga'
print a1   #=> [['hoge', 2, 3], ['hoge', 2, 3], ['hoge', 2, 3]]
print a2   #=> [['fuga', 2, 3], ['fuga', 2, 3], ['fuga', 2, 3]]

もちろん Squeak システムの Smalltalkdeep copy は賢くない(笑)ので、

a1 a2
a1 := Array streamContents: [: s | 3 timesRepeat: [s nextPut: #(1 2 3)]]. a2 := a1 deepCopy. (a1 at: 1) at: 1 put: 'hoge'. (a2 at: 1) at: 1 put: 'fuga'. Transcript cr; show: a1. "=> #(('hoge' 2 3) ('hoge' 2 3) ('hoge' 2 3)) " Transcript cr; show: a2 "=> #(('fuga' 2 3) (1 2 3) (1 2 3)) "

となります。


ちなみに、Pythondeep copy の賢さは配列が再帰構造をとっているときに、さらによく分かるようになっています。

import copy
a1 = [1, 2, 3]
a1[0] = a1
a2 = copy.deepcopy(a1)
a2[1] = 'hoge'
print a2[0][1]   #=> 'hoge'
print a1[0][1]   #=> 2

もちろん、Squeak システムの Smalltalkdeep copy は馬鹿なので、

a1 a2
a1 := #(1 2 3). a1 at: 1 put: a1. a2 := a1 deepCopy. "=> ここで無限ループ(alt-/cmd-ピリオドで脱出せよ(笑)) " a2 at: 2 put: 'hoge'. Transcript cr; show: a2 first second. "=> 'hoge' を期待 " Transcript cr; show: a1 first second "=> 2 を期待 "

となります。


もひとつちなみに、VisualWorks では Python と同じ結果になります。


全然関係ないのですが、ここで Ruby のリストにからめた二項演算子振る舞いPython にそっくりなことも分かりました。さらに上流もありそうです。ひまなときに辿ってみましょう。

#(1 2 3) * #(4 5 6)    "=> #(4 10 18) "
[1, 2, 3] * [4, 5, 6]  #=> error

#(1 2 3) * 3    "=> #(3 6 9) "
Array streamContents: [: s | 3 timesRepeat: [s nextPutAll: #(1 2 3)]]
                "=> #(1, 2, 3, 1, 2, 3, 1, 2, 3)
[1, 2, 3] * 3   #=> [1, 2, 3, 1, 2, 3, 1, 2, 3]

#(1 2 3) + 3    "=> #(4 5 6)"
[1, 2, 3] + 3   #=> error

#(1 2 3) + #(4 5 6)     "=> #(5 7 9) "
#(1 2 3), #(4 5 6)      "=> #(1 2 3 4 5 6) "
[1, 2, 3] + [4, 5, 6]   #=> [1, 2, 3, 4, 5, 6]