About shallow and deep copies of Python / Ruby

About shallow and deep copies of Python / Ruby

In Python and Ruby, unlike PHP, assignment is not a copy, and assignment means "creating a binding between the target and the object." I left it as a memorandum to deepen my understanding of the meanings of "immutable" and "mutable", as well as the fact that it was difficult to understand the meaning of "creating a binding between the target and the object". Here, Python3.6.2 and Ruby2.4.1 were used.

Substitution

Integer substitution

From the results of Python 3.6 and Ruby 2.4 below, when the variable ʻa is manipulated, the ʻid of ʻa changes, and it is immutable (cannot be changed) because it cannot be changed for the integer object 3`. Here, for convenience, such an object is treated as an immutable object.

For Python 3.6

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)))

Output result


id_3:1623654960, id_a:1623654960
id_b:1623654960
id_3:1623654960, id_4:1623654992, id_a:1623654992, id_b:1623654960

In the case of Python3.6, there are cases where ʻid` is different even for integers with the same value as shown below.

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


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

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

Output result


2812564670744
2812564671464

For Ruby 2.4

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]

Output result


id_3:7, id_a:7
id_b:7
id_3:7, id_4:9, id_a:9, id_b:7

Even in the case of Ruby, there are cases where ʻid` is different even for integers with the same value as shown below.

p 99999999999999999999999.object_id
p 99999999999999999999999.object_id

Output result


25133964
25133856

Array (Ruby) / List (Python) assignment

From the results of Python 3.6 and Ruby 2.4 below, even if the value is changed by manipulating ʻa, it is the same ʻid and can be changed to a different value. That is, it is mutable (changeable). Here, for convenience, such an object is treated as a mutable object. When the value of ʻa is assigned to b, ʻid is the same, so if the value is changed by manipulating ʻa, b` also changes.

For Python 3.6

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)

Output result


id_a:4332032456, id_b:4332032456
[1, 2, 3] [1, 2, 3]
id_a:4332032456, id_b:4332032456
[1, 4, 3] [1, 4, 3]

For Ruby 2.4

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

Output result


id_a:70131497645860, id_b:70131497645860
[1, 2, 3]
[1, 2, 3]
id_a:70131497645860, id_b:70131497645860
[1, 4, 3]
[1, 4, 3]

Shallow copy

In the case of a mutable object, as in the above example, if the value of ʻa is assigned to b, the ʻid is the same, so if the value is changed by operating ʻa, b Also changes. You need to make a copy to make the value of b independent of ʻa. In the case of the list or array in this example, it can be realized by making a shallow copy. In the example below, by making a shallow copy, the value of b does not change even if the value is changed by the operation of ʻa`.

For Python 3.6

A shallow copy can be made by substituting the copy.copy function or ʻa [:]`.

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)

Output result


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]

For Ruby 2.4

A shallow copy can be achieved by using ʻObject.clone or ʻObject.dup. In addition, [here](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) The behavior is different from ActiveModel.clone and ʻActiveModel.dup of Rails`.

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

Output result


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]

Cases where shallow copying is not enough

For example, in the following example where the element of the list is a list and the element of the array is an array, that is, there is another mutable object from the mutable object, even if you make a shallow copy, you can operate ʻa [0] . The value of b [0]` is changed. This behaves the same for both Python 3.6 and Ruby 2.4.

For Python 3.6

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)

Output result


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']

For Ruby 2.4

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

Output result


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"]

Deep copy

If a mutable object holds another mutable object, which is a "shallow copy is not enough" case, you need to make a deep copy instead of a shallow copy to completely copy the value of b to ʻa`. There is.

For Python 3.6

A deep copy can be achieved with copy.deepcopy.

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)

Output result


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']

For Ruby 2.4

A deep copy can be achieved with Marshal.load and Marshal.dump.

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

Output result


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"]

Supplement

About strings in Ruby

In Ruby 2.4, string literals are mutable objects as shown below. As far as I refer to here, string literals are immutable objects in Ruby3. In Python3.6, it is necessary to convert to list type as b = list (a) in order to handle as follows.

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

Output result


id_a:70186456022160
id_a:70186456022160

About assignment by PHP reference

In the case of PHP, does the assignment correspond to a deep copy? In the example below, even if $ a = [5];, $ b refers to $ a, so it will be the value referenced by $ a. Substitution by reference such as $ a = & $ b; in PHP cannot be realized in Python or Ruby.

<?php

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

Output result


Array
(
    [0] => 5
)
Array
(
    [0] => 5
)

About shallow and deep copies

In the example here, lists and arrays are taken as examples, but the difference between shallow copy and deep copy can be discriminated even in the following cases.

For Python 3.6

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()

Output result


5
5
7

For Ruby 2.4

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

Output result


5
5
7

Recommended Posts

About shallow and deep copies of Python / Ruby
Python shallow copy and deep copy
Python shallow and deep copy
Correspondence summary of array operation of ruby and python
Specifying the range of ruby and python arrays
Ruby, Python and map
Python and Ruby split
About the * (asterisk) argument of python (and itertools.starmap)
Comparison of Python and Ruby (Environment / Grammar / Literal)
[Python] Chapter 01-02 About Python (Execution and installation of development environment)
Comparison of CoffeeScript with JavaScript, Python and Ruby grammar
Version control of Node, Ruby and Python with anyenv
Python on Ruby and angry Ruby on Python
About python objects and classes
About Python variables and objects
About the ease of Python
Python and ruby slice memo
About various encodings of Python 3
About Python, len () and randint ()
About Perl, Python, PHP, Ruby
Ruby and Python syntax ~ branch ~
About Python and regular expressions
About the features of Python
Source installation and installation of Python
About Python and os operations
Python # About reference and copy
About Python sort () and reverse ()
Python vs Ruby "Deep Learning from scratch" Chapter 1 Graph of sin and cos functions
Summary of Hash (Dictionary) operation support for Ruby and Python
Environment construction of python and opencv
Difference between Ruby and Python split
The story of Python and the story of NaN
Installation of SciPy and matplotlib (Python)
Scraping with Node, Ruby and Python
About python dict and sorted functions
About dtypes in Python and Cython
About Python pickle (cPickle) and marshal
[Python] About Executor and Future classes
About Python, from and import, as
This and that of python properties
Coexistence of Python2 and 3 with CircleCI (1.0)
About the basics list of Python basics
Summary of Python indexes and slices
Reputation of Python books and reference books
I compared the speed of Hash with Topaz, Ruby and Python
Installation of Visual studio code and installation of python
About building GUI using TKinter of Python
Differences between Ruby and Python in scope
Extraction of tweet.js (json.loads and eval) (Python)
Meaning of deep learning models and parameters
About the virtual environment of python version 3.7
About sensor_mode and angle of view of picamera
Memorandum of python beginners About inclusion notation
Connect a lot of Python or and and
Encrypt with Ruby (Rails) and decrypt with Python
A story about Python pop and append
Easy introduction of python3 series and OpenCV3
[Python] Various combinations of strings and values
Easy web scraping with Python and Ruby
Memo of troubles about coexistence of Python 2/3 system
[Python] Chapter 02-04 Basics of Python Program (About Comments)