Python/Rubyの浅いコピーと深いコピーについて

#Python/Rubyの浅いコピーと深いコピーについて
PHPとは違いPythonとRubyでは代入がコピーではなく、代入は「ターゲットとオブジェクトの間に束縛を作る」ことを意味する。「ターゲットとオブジェクトの間に束縛を作る」という意味が分かりづらいかったということと、「イミュータブル」と「ミュータブル」という意味について、サンプルをベースに理解を深めるために備忘録として残した。
ここでは、Python3.6.2、Ruby2.4.1を利用した。

##代入
###整数の代入
以下のPython3.6とRuby2.4の結果から、変数`a`を操作すると`a`の`id`が変わり、`3`という整数オブジェクトに対して変更できないためイミュータブル（変更不可）である。ここでは便宜上、このようなオブジェクトをイミュータブルオブジェクトとして扱う。

####Python3.6の場合

```py3
a = 3
print("id_3:%d, id_a:%d" % (id(3), id(a)))
b = a
print("id_b:%d" % id(b))
a += 1
print("id_3:%d, id_4:%d, id_a:%d, id_b:%d" % (id(3), id(4), id(a), id(b)))
```

```text:出力結果
id_3:1623654960, id_a:1623654960
id_b:1623654960
id_3:1623654960, id_4:1623654992, id_a:1623654992, id_b:1623654960
```

Python3.6の場合、下記のように同じ値の整数でも`id`が異なるケースがある。

```py3
class A:
    def get(self):
        print(id(99999999999999999999999999999))


class B:
    def get(self):
        print(id(99999999999999999999999999999))

A().get()
B().get()
```

```text:出力結果
2812564670744
2812564671464
```

####Ruby2.4の場合

```rb
a = 3
puts 'id_3:%d, id_a:%d' % [3.object_id, a.object_id]
b = a
puts 'id_b:%d' % [b.object_id]
a += 1
puts 'id_3:%d, id_4:%d, id_a:%d, id_b:%d' % [3.object_id, 4.object_id, a.object_id, b.object_id]
```

```text:出力結果
id_3:7, id_a:7
id_b:7
id_3:7, id_4:9, id_a:9, id_b:7
```

なお、Rubyの場合でも、下記のように同じ値の整数でも`id`が異なるケースがある。

```rb
p 99999999999999999999999.object_id
p 99999999999999999999999.object_id
```

```text:出力結果
25133964
25133856
```


###配列（Ruby）・リスト（Python）の代入
以下のPython3.6とRuby2.4の結果から、`a`を操作して値が変更しても同じ`id`であり、異なる値に変更することができる。すなわち、ミュータブル（変更可）である。ここでは便宜上、このようなオブジェクトをミュータブルオブジェクトとして扱う。
`b`に`a`の値を代入した場合、`id`が同じため、`a`を操作することにより値が変更された場合、`b`も変わる。

####Python3.6の場合

```py3
a = [1,2,3]
b = a
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 4
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)
```

```text:出力結果
id_a:4332032456, id_b:4332032456
[1, 2, 3] [1, 2, 3]
id_a:4332032456, id_b:4332032456
[1, 4, 3] [1, 4, 3]
```

####Ruby2.4の場合

```rb
a = [1,2,3]
b = a
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 4
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b
```

```text:出力結果
id_a:70131497645860, id_b:70131497645860
[1, 2, 3]
[1, 2, 3]
id_a:70131497645860, id_b:70131497645860
[1, 4, 3]
[1, 4, 3]
```

##浅いコピー
ミュータブルオブジェクトの場合、上述の例のように、`b`に`a`の値を代入した場合、`id`が同じため、`a`を操作することにより値が変更された場合、`b`も変わってしまう。`b`の値を`a`と独立させるにはコピーをする必要がある。この例でのリストや配列の場合、浅いコピーを施すことで実現できる。
下記の例では浅いコピーをすることで、`a`の操作で値が変更されても、`b`の値は変わらない。

###Python3.6の場合
`copy.copy`関数または`a[:]`を代入することで浅いコピーができる。

```py3
import copy as cp

a = [1,2,3]
b = cp.copy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 4
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

b = a[:]
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 3
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)
```

```text:出力結果
id_a:4460690760, id_b:4460690888
[1, 2, 3] [1, 2, 3]
id_a:4460690760, id_b:4460690888
[1, 4, 3] [1, 2, 3]
id_a:4460690760, id_b:4460691272
[1, 4, 3] [1, 4, 3]
id_a:4460690760, id_b:4460691272
[1, 3, 3] [1, 4, 3]
```

###Ruby2.4の場合
`Object.clone`または`Object.dup`を利用することで浅いコピーが実現できる。
なお、[こちら](https://ja.stackoverflow.com/questions/27101/ruby%E3%81%AEclone%E3%81%A8dup%E3%81%A8activerecord%E3%81%AEclone%E3%81%A8dup%E3%81%AF%E5%88%A5%E7%89%A9)のように`Rails`の`ActiveModel.clone`と`ActiveModel.dup`と挙動が異なる。

```rb
a = [1,2,3]
b = a.clone
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 4
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

b = a.dup
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 3
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b
```

```text:出力結果
id_a:70245067666580, id_b:70245067666520
[1, 2, 3]
[1, 2, 3]
id_a:70245067666580, id_b:70245067666520
[1, 4, 3]
[1, 2, 3]
id_a:70245067666580, id_b:70245067665580
[1, 4, 3]
[1, 4, 3]
id_a:70245067666580, id_b:70245067665580
[1, 3, 3]
[1, 4, 3]
```

###浅いコピーでは不十分となるケース
例えばリストの要素にリスト、配列の要素に配列がある場合、すなわちミュータブルオブジェクトからさらに別のミュータブルオブジェクトがあるような下記の例では、浅いコピーをしても`a[0]`を操作により、`b[0]`の値が変更されてしまう。これはPython3.6もRuby2.4も同一の挙動となる。

####Python3.6の場合

```py3
import copy as cp

a = [[1,2,3], "abc"]
b = cp.copy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 4
a[1] = "def"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

b = a[:]
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 3
a[1] = "abc"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)
```

```text:出力結果
id_a:4393106888, id_b:4393107272
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4391239728, id_b[1]:4391239728
[[1, 2, 3], 'abc'] [[1, 2, 3], 'abc']
id_a:4393106888, id_b:4393107272
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4392739984, id_b[1]:4391239728
[[1, 4, 3], 'def'] [[1, 4, 3], 'abc']
id_a:4393106888, id_b:4393112648
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4392739984, id_b[1]:4392739984
[[1, 4, 3], 'def'] [[1, 4, 3], 'def']
id_a:4393106888, id_b:4393112648
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4391239728, id_b[1]:4392739984
[[1, 3, 3], 'abc'] [[1, 3, 3], 'def']
```

####Ruby2.4の場合

```rb
a = [[1,2,3], "abc"]
b = a.clone
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 4
a[1] = "def"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

b = a.dup
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 3
a[1] = "abc"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b
```

```text:出力結果
id_a:70199714913920, id_b:70199714913860
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714913940, id_b[1]:70199714913940
[[1, 2, 3], "abc"]
[[1, 2, 3], "abc"]
id_a:70199714913920, id_b:70199714913860
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714912900, id_b[1]:70199714913940
[[1, 4, 3], "def"]
[[1, 4, 3], "abc"]
id_a:70199714913920, id_b:70199714912200
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714912900, id_b[1]:70199714912900
[[1, 4, 3], "def"]
[[1, 4, 3], "def"]
id_a:70199714913920, id_b:70199714912200
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714911480, id_b[1]:70199714912900
[[1, 3, 3], "abc"]
[[1, 3, 3], "def"]
```

##深いコピー
「浅いコピーでは不十分なケース」となるミュータブルオブジェクトが別のミュータブルオブジェクトを保持するような場合、`b`の値を`a`と完全にコピーさせるには浅いコピーではなく深いコピーをする必要がある。

###Python3.6の場合
`copy.deepcopy`で深いコピーが実現できる。

```py3
import copy as cp

a = [[1,2,3], "abc"]
b = cp.deepcopy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 4
a[1] = "def"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)
```

```text:出力結果
id_a:4306767304, id_b:4306767688
id_a[0]:4306767176, id_b[0]:4306773064
id_a[1]:4304900144, id_b[1]:4304900144
[[1, 2, 3], 'abc'] [[1, 2, 3], 'abc']
id_a:4306767304, id_b:4306767688
id_a[0]:4306767176, id_b[0]:4306773064
id_a[1]:4306400400, id_b[1]:4304900144
[[1, 4, 3], 'def'] [[1, 2, 3], 'abc']
```

###Ruby2.4の場合
`Marshal.load`と`Marshal.dump`で深いコピーが実現できる。

```rb
a = [[1,2,3], "abc"]
b = Marshal.load(Marshal.dump(a))
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 4
a[1] = "def"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b
```

```text:出力結果
id_a:70274700700880, id_b:70274700700800
id_a[0]:70274700700940, id_b[0]:70274700700780
id_a[1]:70274700700900, id_b[1]:70274700700760
[[1, 2, 3], "abc"]
[[1, 2, 3], "abc"]
id_a:70274700700880, id_b:70274700700800
id_a[0]:70274700700940, id_b[0]:70274700700780
id_a[1]:70274700699900, id_b[1]:70274700700760
[[1, 4, 3], "def"]
[[1, 2, 3], "abc"]
```

##補足
###Rubyでの文字列について
Ruby2.4では下記のように文字列リテラルはミュータブルオブジェクトとなる。[こちら](https://bugs.ruby-lang.org/issues/11473)を参照する限りでは、文字列リテラルはRuby3でイミュータブルオブジェクトになるとのこと。
なお、Python3.6では、下記のように扱うには`b = list(a)`とリスト型に変換する必要がある。

```rb
a = "abc"
puts 'id_a:%d' % [a.object_id]
a[0] = "A"
puts 'id_a:%d' % [a.object_id]
```

```text:出力結果
id_a:70186456022160
id_a:70186456022160
```

###PHPの参照による代入について
PHPの場合、代入が深いコピーに相当するだろうか。なお、下記例では、`$a = [5];`としても、`$b`は`$a`を参照しているため、`$a`の参照する値となる。このようなPHPの`$a = &$b;`のような参照による代入はPythonやRubyでは実現できない。

```php
<?php

$a = [3];
$b = &$a;
$a = [5];
print_r($a);
print_r($b);
```

```text:出力結果
Array
(
    [0] => 5
)
Array
(
    [0] => 5
)
```

###浅いコピーと深いコピーについて
ここでの例ではリスト、配列を例にしたが、下記のようなケースでも浅いコピーと深いコピーの違いが判別できる。

####Python3.6の場合

```py3
import copy


class Test1:
    def __init__(self):
        self.a = 0

    def set(self, n):
        self.a = n

    def get(self):
        print(self.a)


class Test2:
    def __init__(self, t):
        self.t1 = t

    def set(self, n):
        self.t1.set(n)

    def get(self):
        self.t1.get()


a = Test2(Test1())
b = copy.copy(a)
c = copy.deepcopy(a)

a.set(3)
b.set(5)
c.set(7)

a.get()
b.get()
c.get()
```

```text:出力結果
5
5
7
```

####Ruby2.4の場合

```rb
class Test1
  def initialize
    @a = 0
  end

  def set(n)
    @a = n
  end

  def get()
    puts @a
  end
end

class Test2
  def initialize(t1)
    @t1 = t1
  end

  def set(n)
    @t1.set(n)
  end

  def get()
    @t1.get
  end
end

a = Test2.new(Test1.new)
b = a.clone
c = Marshal.load(Marshal.dump(a))

a.set(3)
b.set(5)
c.set(7)

a.get
b.get
c.get
```

```text:出力結果
5
5
7
```