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.
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.
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
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
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.
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]
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]
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`.
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]
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]
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.
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']
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"]
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.
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']
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"]
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
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
)
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.
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
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