2
0

More than 3 years have passed since last update.

numpy.arrayインプレース演算の落とし穴(Python)

Posted at

概要

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の配列を使う際に、気を付けようと思った。

今後

代入演算子に注意。

2
0
0

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
2
0