モーグルとカバとパウダーの日記

モーグルやカバ(EXカービング)山スキー(BC)などがメインの日記でした。今は仕事のコンピュータ系のネタが主になっています。以前はスパム対策関連が多かったのですが最近はディープラーニング関連が多めです。

numpyで多次元配列の一部を渡した時の動作について

※追記で解決しました


最近、趣味でニューラルネット系のことをやっているためnumpyを触っているのですが、不可解な動作をすることがありました。

a = np.array([[1, 2], [3, 4]])

def test2(x):
  test1(x[0])

def test1(x):
  x += np.array([2, 3])

test2(a)

これでaを表示すると、期待通り

array([[3, 5], [3, 4]])

となります。


がtest1の関数を

def test1(x):
  x = x + np.array([2, 3])

のように「x += …」を「x = x + …」と書いてしまうと

array([[1, 2], [3, 4]])

のように計算結果が保存されないのです。


どうやら「x += …」の場合は多次元配列の中身への参照として扱われるのですが「x = x + …」でやってしまうと値渡しとして扱われてしまっている?ようです。


Pythonの引数は全部参照渡しとのことなのですが、intやstringのようなプリミティブなものはImmutableとして扱われるとかなってて、渡されるものによって動作が変わるようでハマる元な感じです。

Pythonでの引数の取り扱いの罠等


ただ今回のこの例の場合は、numpyが「+=」と「=」をオーバーライドしてる中の処理の違いが問題になってるのでは、という指摘をいただきました。


Cのポインター的に、多次元配列の一部を取り出したものを明示的に参照渡しするような書式があったらうれしいのですが。


(追記 2017/1/26)

配列から一部を取り出すスライス記法(r=o[0:2, 1:3]みたいにして多次元配列の範囲を取得できる)を試してたときも同じような状況になって、困ってたらわかりました。


NumPy配列のスライス表記の参照と代入
https://hydrocul.github.io/wiki/numpy/ndarray-ref-slice.html


その取得した範囲全体に対して値を入れる場合には

r = o[0:2, 1:3]
r[:] = 0

とやるとその選択範囲を0に出来るとのことでした。


そこで同様に考えて

r = o[0:2, 1:3]
# r *= 2 と同じ
r[:] = r*2

とやるとその範囲の値だけを全て2倍にできました。