NumPy阵列拷贝写入

时间:2014-02-20 01:06:03

标签: python numpy copy-on-write

我有一个返回大型NumPy数组的类。这些数组缓存在类中。我希望返回的数组是copy-on-write数组。如果调用者最终只是从数组中读取,则不会进行任何复制。这种情况下不会使用额外的内存。但是,该数组是“可修改的”,但不会修改内部缓存的数组。

我目前的解决方案是使任何缓存的数组只读(a.flags.writeable = False)。这意味着如果函数的调用者可能必须创建自己的数组副本,如果他们想要修改它。当然,如果源不是来自缓存而且数组已经可写,那么它们会不必要地复制数据。

所以,最好的我会喜欢像a.view(flag=copy_on_write)这样的东西。似乎有一个标记用于此UPDATEIFCOPY的反向,这会导致副本在取消分配后更新原始文件。

谢谢!

2 个答案:

答案 0 :(得分:4)

写时复制是一个很好的概念,但显式复制似乎是“NumPy哲学”。所以,如果不是太笨拙,我会保留“只读”解决方案。

但我承认编写了自己的写时复制包装类。我不会尝试检测对数组的写访问权限。相反,该类有一个方法“get_array(readonly)”返回其(否则是私有的)numpy数组。第一次使用“readonly = False”调用它时,它会复制一份。这非常明确,易于阅读和快速理解。

如果你的写时复制numpy数组看起来像一个经典的numpy数组,你的代码的读者(可能你在2年内)可能会遇到困难。

答案 1 :(得分:3)

要在写入时实现副本,我们需要修改ndarray对象的basedatastrides。我认为这不能用纯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 ]