2014-02-20 59 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