NumPy配列ndarrayをイミュータブル(書き換え禁止)に設定
NumPy配列numpy.ndarrayは基本的にミュータブルで要素を指定して値を更新可能。numpy.ndarrayの設定を変更することでイミュータブル(書き換え禁止, read-only)にできる。
誤って値を更新してしまうのを防ぐのに便利。
ここでは以下の内容について説明する。
ndarrayのメモリレイアウト情報を格納しているflagsWRITEABLE属性で配列をイミュータブル(書き換え禁止)に設定WRITEABLE属性を変更できない場合もあり
最後に説明するように、元の配列が書き換え可能である場合は、そのビューを書き換え禁止にしても元の配列から要素の値が更新できるので注意。
ndarrayのメモリレイアウト情報を格納しているflags
numpy.ndarrayのメモリレイアウトの情報はflagsに格納されている。
import numpy as np
a = np.arange(3)
print(a)
# [0 1 2]
print(a.flags)
# C_CONTIGUOUS : True
# F_CONTIGUOUS : True
# OWNDATA : True
# WRITEABLE : True
# ALIGNED : True
# WRITEBACKIFCOPY : False
# UPDATEIFCOPY : False
print(type(a.flags))
# <class 'numpy.flagsobj'>
numpy.flagsobjという独自の型だが、.属性名や['属性名']として各属性の値を確認できる。.属性名は小文字で['属性名']は大文字なので注意。
print(a.flags.writeable)
# True
print(a.flags['WRITEABLE'])
# True
WRITEABLE属性で配列をイミュータブル(書き換え禁止)に設定
numpy.ndarrayが書き換え禁止かどうかの設定はWRITEABLE属性。
上の例のように新たにnumpy.ndarrayを作成した場合、WRITEABLEはTrueに設定されており、値を更新することが可能。
a[0] = 100
print(a)
# [100 1 2]
WRITEABLEをFalseとすると書き換え禁止となり、値を更新しようとするとエラーとなる。
a.flags.writeable = False
# a[0] = 0
# ValueError: assignment destination is read-only
.writeableや['WRITEABLE']で変更するほか、setflags()というメソッドでも設定値を変更できる。setflags()では引数writeがWRITEABLEに対応している。
a.flags['WRITEABLE'] = False
a.setflags(write=False)
WRITEABLE属性を変更できない場合もあり
WRITEABLE属性は常に変更できるわけではない。
例えば、スライスなどでnumpy.ndarray配列のビューを生成した場合、元の配列が書き換え禁止(WRITEABLEがFalse)だとビューも書き換え禁止となる。
a = np.arange(3)
print(a)
# [0 1 2]
a.flags.writeable = False
a_view = a[1:]
print(a_view)
# [1 2]
print(a_view.flags.writeable)
# False
# a_view[0] = 100
# ValueError: assignment destination is read-only
元の配列が書き換え禁止の場合、ビューのWRITEABLE属性をTrueに変更して書き換え禁止を解除することはできない。
# a_view.flags.writeable = True
# ValueError: cannot set WRITEABLE flag to True of this array
元の配列のWRITEABLE属性をTrueにしてもビューのWRITEABLE属性はFalseのまま変わらないが、Trueに変更できるようになる。
a.flags.writeable = True
print(a_view.flags.writeable)
# False
a_view.flags.writeable = True
a_view[0] = 100
print(a_view)
# [100 2]
print(a)
# [ 0 100 2]
ビューのWRITEABLE属性がFalseであっても、元の配列のWRITEABLE属性がTrueであれば元の配列からは更新可能。注意。
a_view.flags.writeable = False
# a_view[1] = 1
# ValueError: assignment destination is read-only
print(a.flags.writeable)
# True
a[1] = 1
print(a)
# [0 1 2]
print(a_view)
# [1 2]
コピーの場合は新たな配列が生成されるので元の配列とは独立してWRITEABLE属性を設定可能。
a.flags.writeable = False
a_copy = a[1:].copy()
print(a_copy)
# [1 2]
print(a_copy.flags.writeable)
# True
a_copy[0] = 100
print(a_copy)
# [100 2]
print(a)
# [0 1 2]
ビューとコピーの判定などについては以下の記事を参照。