2014-02-20 56 views
4

我有一个返回大型NumPy数组的类。这些数组缓存在类中。我希望返回的数组是写入时复制数组。如果调用者只是从数组中读取数据,则不会创建副本。这将不会使用额外的内存。但是,该数组是“可修改的”,但不会修改内部缓存阵列。NumPy Array写入时复制

我现在的解决方案是使任何缓存阵列只读(a.flags.writeable = False)。这意味着如果函数的调用者可能需要制作他们自己的阵列副本(如果他们想修改它的话)。当然,如果源不是来自缓存并且该数组已经可写,那么它们将不必要地复制数据。

所以,最好我喜欢a.view(flag=copy_on_write)之类的东西。似乎有一个与此UPDATEIFCOPY相反的标志,它会导致副本更新原来的一次解除分配。

谢谢!

回答

3

写入时复制是一个很好的概念,但明确的复制似乎是“NumPy的哲学”。因此,如果个人不太笨拙,我会保留“只读”解决方案。

但我承认自己写了我自己写的copy-on-write包装类。我不试图检测对阵列的写入权限。相反,类有一个方法“get_array(只读)”返回其(否则私人)numpy数组。第一次用“readonly = False”来调用时,它会复制一份。这是非常明确的,易于阅读和迅速理解。

如果您的copy-on-write numpy数组看起来像经典的numpy数组,那么您的代码的读者(可能在2年内)可能很难。

+1

我已经用这种方法,除了它总是只读的,如果调用者想要它读写,他们可以自己复制它。 – coderforlife

3

为了在写入时实现复制,我们需要修改ndarray对象的base,datastrides。我认为这不能在纯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 ] 
+1

我一直在尝试这个,它看起来相当不错。但是有一些“问题”。首先,我必须在Cython代码中的if语句之前添加一个while循环,以继续搜索基础,直到找到匹配或None,其他方法如下:'b = a.view(cowarray); C = B [2]; c [0] = 1000;'不会复制。但也有可能存在其他问题,例如循环修复可能需要更多的副本。 – coderforlife