#概要
Python numpyでインプレース演算(+=
など)をした際に思わぬ挙動をすることがあるので、
その確認をする。
#内容
##環境
macOS Catalina
Python 3.7.0
numpy 1.18.3
##再現
>>> import numpy as np
>>> a = np.array([3])
>>> b, c = a, a
>>> a, b, c
(array([3]), array([3]), array([3]))
>>> c += 1
>>> a, b, c
(array([4]), array([4]), array([4]))
##分析
a
, b
, c
が参照しているのは、np.array
オブジェクトに対してであるが、
+=1
が実は変更したのは、その属性であるからである。
下記のように、+=1
がオブジェクト自体を変更する場合は、予想通りの変化になる。
>>> a = 3
>>> b, c = a, a
>>> a, b, c
(3, 3, 3)
>>> c += 1
>>> a, b, c
(3, 3, 4)
>>>
##解決策
c = c + 1
とし、オブジェクトを新たに生成する。
>>> import numpy as np
>>> a = np.array([3])
>>> b, c = a, a
>>> c = c + 1
>>> a, b, c
(array([3]), array([3]), array([4]))
##蛇足
id
関数を使って確認することもできる。
>>> import numpy as np
>>> a = np.array([3])
>>> b, c = a, a
>>> id(a), id(b), id(c)
(4692042016, 4692042016, 4692042016)
>>> c += 1
>>> a, b, c
(array([4]), array([4]), array([4]))
>>> id(a), id(b), id(c)
(4692042016, 4692042016, 4692042016)
>>> c = c + 1
>>> a, b, c
(array([4]), array([4]), array([5]))
>>> id(a), id(b), id(c)
(4692042016, 4692042016, 4692041936)
なお、属性の直接の変更は、下記のように同一オブジェクトを参照している場合は、どちらも変わって見える。
インプレースかどうかの問題ではなく、オブジェクトと参照の問題であるはず。(言葉足らず)
>>> class Obj: pass
...
>>> a = Obj()
>>> a.name = 'mori'
>>> b = a
>>> a.name, b.name
('mori', 'mori')
>>> a.name += 'ta'
>>> a.name, b.name
('morita', 'morita')
>>> a.name = a.name + 'rou'
>>> a.name, b.name
('moritarou', 'moritarou')
#参考にさせていただいた本
『ゼロから作るDeep Learning③ フレームワーク編』斎藤康毅著 O'REILLY
#感想
numpyの配列を使う際に、気を付けようと思った。
#今後
代入演算子に注意。