9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonの`+=`はin-placeである

Last updated at Posted at 2021-05-19

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は値型なので(要素変更で元の配列が書き換わることがないため)、例として不適切でした。削除しました。

9
5
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?