我有一个返回大型NumPy数组的类。这些数组缓存在类中。我希望返回的数组是copy-on-write数组。如果调用者最终只是从数组中读取,则不会进行任何复制。这种情况下不会使用额外的内存。但是,该数组是“可修改的”,但不会修改内部缓存的数组。
我目前的解决方案是使任何缓存的数组只读(a.flags.writeable = False)
。这意味着如果函数的调用者可能必须创建自己的数组副本,如果他们想要修改它。当然,如果源不是来自缓存而且数组已经可写,那么它们会不必要地复制数据。
所以,最好的我会喜欢像a.view(flag=copy_on_write)
这样的东西。似乎有一个标记用于此UPDATEIFCOPY
的反向,这会导致副本在取消分配后更新原始文件。
谢谢!
答案 0 :(得分:4)
写时复制是一个很好的概念,但显式复制似乎是“NumPy哲学”。所以,如果不是太笨拙,我会保留“只读”解决方案。
但我承认编写了自己的写时复制包装类。我不会尝试检测对数组的写访问权限。相反,该类有一个方法“get_array(readonly)”返回其(否则是私有的)numpy数组。第一次使用“readonly = False”调用它时,它会复制一份。这非常明确,易于阅读和快速理解。
如果你的写时复制numpy数组看起来像一个经典的numpy数组,你的代码的读者(可能你在2年内)可能会遇到困难。
答案 1 :(得分:3)
要在写入时实现副本,我们需要修改ndarray对象的base
,data
,strides
。我认为这不能用纯Python代码完成。我用了一些Cython
用于修改这些属性的代码。
以下是IPython笔记本中的代码:
%load_ext cythonmagic
使用Cython定义copy_view()
:
%%cython
cimport numpy as np
np.import_array()
np.import_ufunc()
def copy_view(np.ndarray a):
cdef np.ndarray b
cdef object base
cdef int i
base = np.get_array_base(a)
if base is None or isinstance(base, a.__class__):
return a
else:
print "copy"
b = a.copy()
np.set_array_base(a, b)
a.data = b.data
for i in range(b.ndim):
a.strides[i] = b.strides[i]
定义ndarray的子类:
class cowarray(np.ndarray):
def __setitem__(self, key, value):
copy_view(self)
np.ndarray.__setitem__(self, key, value)
def __array_prepare__(self, array, context=None):
if self is array:
copy_view(self)
return array
def __array__(self):
copy_view(self)
return self
一些测试:
a = np.array([1.0, 2, 3, 4])
b = a.view(cowarray)
b[1] = 100 #copy
print a, b
b[2] = 200 #no copy
print a, b
c = a[::2].view(cowarray)
c[0] = 1000 #copy
print a, c
d = a.view(cowarray)
np.sin(d, d) #copy
print a, d
输出:
copy
[ 1. 2. 3. 4.] [ 1. 100. 3. 4.]
[ 1. 2. 3. 4.] [ 1. 100. 200. 4.]
copy
[ 1. 2. 3. 4.] [ 1000. 3.]
copy
[ 1. 2. 3. 4.] [ 0.84147098 0.90929743 0.14112001 -0.7568025 ]