Pythonで、listを別の変数に代入後、配列の要素を変更すると、元の配列の要素も変更される。これはRubyでも同様だし、参照値の概念から理解できる。
a=[1]
b=a
b[0]=2
print(a)
print(b)
[2]
[2]
ところがそれだけでなく、+=
を実行すると、元の変数まで書き換わってしまう事象があった。
a=[1]
b=a
print(id(a))
print(id(b))
b+=[2]
print(id(b))
print(a)
print(b)
140675580591136
140675580591136
140675580591136
[1, 2]
[1, 2]
https://docs.python.org/ja/3/library/operator.html#in-place-operators にあるように、+=
がin-placeであるためである。実際に、id()が変化していないことがわかる。
念のため、CPython/PyPy/Jython/IronPython/Nuitka/Grumpyでこの動作になることを確認。
他の言語
Ruby / Crystal
a=[1]
b=a
b[0]=2
p a
p b
[2]
[2]
また、object_idが利用できる。
a=[1]
b=a
p a.object_id
p b.object_id
b+=[2]
p b.object_id
p a
p b
Rubyの出力
47281727614540
47281727614540
47281727457620
[1]
[1, 2]
Crystalの出力
140128848109184
140128848109184
140128848109120
[1]
[1, 2]
+=によりobject_idが変化していることがわかる。
なおArray#concat
を使った場合Pythonの+=
と同じ動作になる。
a=[1]
b=a
b.concat([2])
p a
p b
[1, 2]
[1, 2]
D
Dではobject_idは利用できない(変数のポインタにid的意味はない)が、要素変更で元の配列が書き換わり、連結で元の配列が書き換わらないことを示せば目的としては十分である。
import std.stdio;
void main(){
int []a = [1];
int []b = a;
b[0] = 2;
writeln(a);
writeln(b);
}
[2]
[2]
import std.stdio;
void main(){
int []a = [1];
int []b = a;
b ~= [2];
writeln(a);
writeln(b);
}
[1]
[1, 2]
Scala
Scalaではobject_idは利用できない(以下同様)
object Wandbox {
def main(args: Array[String]): Unit = {
var a = Array(1)
var b = a
b(0) = 2
println(a.mkString(" "))
println(b.mkString(" "))
}
}
2
2
object Wandbox {
def main(args: Array[String]): Unit = {
var a = Array(1)
var b = a
b ++= Array(2)
println(a.mkString(" "))
println(b.mkString(" "))
}
}
1
1 2
Groovy
def a = [1]
def b = a
b[0] = 2
println a
println b
[2]
[2]
def a = [1]
def b = a
b += [2]
println a
println b
[1]
[1, 2]
Kuin
func print(a: []int)
for i(0, ^a-2)
do cui@print(a[i].toStr())
do cui@print(" ")
end for
do cui@print(a[^a-1].toStr())
do cui@print("\n")
end func
func main()
var a: []int :: [1]
var b: []int :: a
do b[0] :: 2
do @print(a)
do @print(b)
end func
2
2
func print(a: []int)
for i(0, ^a-2)
do cui@print(a[i].toStr())
do cui@print(" ")
end for
do cui@print(a[^a-1].toStr())
do cui@print("\n")
end func
func main()
var a: []int :: [1]
var b: []int :: a
do b :~ [2]
do @print(a)
do @print(b)
end func
1
1 2
+=(に相当するもの)がin-placeでないことがわかる。この違いを把握していないとPythonでははまるかもしれない。
Swiftの例を掲載していましたが、SwiftのArrayは値型なので(要素変更で元の配列が書き換わることがないため)、例として不適切でした。削除しました。