Closed3

Pythonにおけるコロンを使ったインプレース操作について

kossykossy

変数aと同じ参照先を持っていた変数bに新しいリストを代入すると、bの参照先が変更されるので、変数aは変更前と同じになります。

a = [1, 2, 3]
b = a
print("id(a): ", id(a))
print("id(b): ", id(b))
print("="*50)

b = [100, 200, 300]
print("a: ", a)  # 出力: [1, 2, 3]
print("id(a): ", id(a))
print("id(b): ", id(b))

出力例

id(a):  138318302100096
id(b):  138318302100096
==================================================
a:  [1, 2, 3]
id(a):  138318302100096
id(b):  138318301143424

一方で、[:]を使って新しいリストを代入すると、既存のオブジェクト自体の中身を直接変更することになるので、変数baと同じ参照先のままで、リストの内容の変更も反映されます。

a = [1, 2, 3]
b = a 
print("id(a): ", id(a))
print("id(b): ", id(b))
print("="*50)

b[:] = [100, 200, 300] # インプレース操作
print("a: ", a)  # 出力: [100, 200, 300]
print("id(a): ", id(a))
print("id(b): ", id(b)) # a と同じ

出力例

id(a):  138318299044288
id(b):  138318299044288
==================================================
a:  [100, 200, 300]
id(a):  138318299044288
id(b):  138318299044288

このように、元々あるオブジェクト自体を直接変更することをインプレース(in-place)操作といい、Pythonでは[:]を使うことで元のオブジェクトの内容を上書きすることができます。

kossykossy

オブジェクトはそのままにして、中身だけを他のリストで上書きしたいときにもインプレース操作を使用できます。

a = [1, 2, 3]
b = [4, 5, 6]
print("id(a): ", id(a))
print("id(b): ", id(b))
print("="*50)

a[:] = b        # a の中身を b で上書き(a 自体は同じオブジェクト)
print("a: ", a) # [4, 5, 6]
print("id(a): ", id(a)) # 元のオブジェクトのまま
print("id(b): ", id(b))

出力例

id(a):  138318299795712
id(b):  138318299791616
==================================================
a:  [4, 5, 6]
id(a):  138318299795712
id(b):  138318299791616
kossykossy

listnumpy.ndarrayでスライス時の挙動が異なる場合

listのスライスは常に新しいオブジェクトを生成します。

a = [1, 2, 3]
b = [4, 5, 6]
b = a[:]  # スライス表記でlistのコピーを渡すことになる
print("a: ", a)
print("b: ", b)
print("id(a): ", id(a))
print("id(b): ", id(b))

a[0] = 100
print("a: ", a)
print("b: ", b)
a:  [1, 2, 3]
b:  [1, 2, 3]
id(a):  138318290623104
id(b):  138318286371392
a:  [100, 2, 3]
b:  [1, 2, 3]

一方、numpy.ndarrayでスライス表記を用いてb = a[:]と記述すると、ビュー(view)として新しいオブジェクトを返します。abは異なるPythonオブジェクトなのでid()は異なっていますが、使用しているメモリ領域が同じなので、ab両方に変更が反映されます。numpy.ndarray.ctypes.dataで使用しているメモリ領域のポインタを調べてみると、abで等しいことが分かります。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
b = a[:]
print("a: ", a)
print("b: ", b)
print("id(a): ", id(a))
print("id(b): ", id(b)) # aと異なる
print("a.ctypes.data:", a.ctypes.data)
print("b.ctypes.data:", b.ctypes.data) # 使用しているメモリ領域のポインタがaと同じ

a[0] = 100
print("a: ", a)
print("b: ", b) # 注意: bにも反映される
a:  [1 2 3]
b:  [1 2 3]
id(a):  138318286318704
id(b):  138318286349360
a.ctypes.data: 54965312
b.ctypes.data: 54965312
a:  [100   2   3]
b:  [100   2   3]

viewではなくデータをコピーしたい場合はb = a.copy()のように記述する必要があります。

参考サイト

参照日は2025/02/01です。

  1. 株式会社Spot「NumPyのコピー(copy)とビュー(view)を分かりやすく解説」https://deepage.net/features/numpy-copyview.html
  2. numpy.ndarray.ctypesに関するドキュメント https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ctypes.html#numpy.ndarray.ctypes
このスクラップは2025/02/01にクローズされました