12
8

More than 3 years have passed since last update.

Python の `+=` は in-place だったりそうじゃなかったりする

Last updated at Posted at 2021-05-19

これは何?

を読んで面白かったので色々試した。

前提

前述の記事からリンクされている文書 にある通り、

文字列、数、タプルのようなイミュータブルなターゲットでは、更新された値が計算されますが、入力変数に代入し返されはしません。
【略】
リストや辞書のようなミュータブルなターゲットでは、インプレースメソッドは更新を行うので、その後に代入をする必要はありません。

となっている。なので前述の記事のとおり、mutable なオブジェクトの場合

Python3
b=a;a+=x;print(b)
Python3
b=a;a=a+x;print(b)

では異なる結果が得られる。a+=x って a=a+x と同じだよね、と思い込んでいる人はこの仕様に足をすくわれて苦しむことがあるだろう。

足をすくわれる前に気づいてよかった。

色々試した

list, set, dic

Python3
a=[1];b=a;a+=[2];print(a,b)
#=> [1, 2] [1, 2]
Python3
a={1:2};b=a;a|={3:4};print(a,b)
#=> {1: 2, 3: 4} {1: 2, 3: 4}
Python3
a={1,2};b=a;a|={3,4};print(a,b)
#=> {1, 2, 3, 4} {1, 2, 3, 4}

このとおり、 += は in-place になる。

string, tuple

Python3
a="xy";b=a;a+="z";print(a,b)
#=> xyz xy
Python3
a=(1,2);b=a;a+=(5,6);print(a,b)
#=> (1, 2, 5, 6) (1, 2)

stringtuple は immutable なので in-place にならない。

ちなみに関係ないけど string% は、%= としても使える(今知った)。

Python3
a="[%s]";b=a;a%="hoge";print(a,b)
#=>[hoge] [%s]

こちらも当然 in-place にはならない。

pathlib.Path

Python3
from pathlib import Path
a=Path("x");b=a;a/="y";print(a,b)
#=> x/y x

pathlib.Path も immutable なのか。メソッド見るとなるほど自分を変更する手段はなさそうだ。

np.array

Python3
a=np.array([1]);b=a;a+=[10];print(a,b)
#=> [11] [11]

ちなみに [:] の動作が組み込み配列と np.array で異なるので

Python3
x=[1];y=x[:];x+=[10];print(x,y)
#=> [1, 10] [1]
x=np.array([1]);y=x[:];x+=[10];print(x,y)
#=> [11] [11]

と、動作が違うので要注意。

byte と bytearray

Python3
x=b'p';y=x;x+=b'q';print(x,y)
#=> b'pq' b'p'
x=bytearray(b'p');y=x;x+=b'q';print(x,y)
#=> bytearray(b'pq') bytearray(b'pq')

bytebytearray で動作が異なる。まあ immutable かどうかで変わると言っているんだからそうだろう。

frozenset

frozenset というものがあるのか。
frozendictfrozenlist はないのかな。

Python3
a=frozenset({1,2});b=a;a|={4};print(a,b)
#=> frozenset({1, 2, 4}) frozenset({1, 2})

set と異なり、immutable なので in-place にはならない。まあそうだよね。

collections の人たち

もっと色々あるかと思ったら、 deque ぐらいしかないのかな。

Python3
a=deque([1,2]);b=a;a+=[3];print(a,b)
#=> deque([1, 2, 3]) deque([1, 2, 3])

date, timedelta

Python3
a=date(2021,2,3);b=a;a+=timedelta(99);print(a,b)
#=> 2021-05-13 2021-02-03

p=timedelta(11);q=p;p+=timedelta(9);print(p,q)
#=> 20 days, 0:00:00 11 days, 0:00:00

datetimedelta も immutable らしい。

まとめ

というわけで、組み込みライブラリ周辺ではちゃんと

  • immutable なら 非 in-place
  • mutable なら in-place

となっていて、このルールからの逸脱は見つけられなかった。

もっと他に +=|=/=^= の類を試したら良さげなものがあったらお知らせください。

12
8
6

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
12
8