<div dir="ltr"><div>I have been working on a general function caching mechanism, and in doing so I stumbled upon the following quirck:</div><div><br></div><div><div>    @cached<div>    def foo(a,b):</div><div>        b[0] = 1</div><div>        return a[0]</div></div><p>    a = np.zeros(1)<div>    b = a[:]</div><div>    print foo(a, b)    #computes and returns 1</div><div>    print foo(a, b)    #gets 1 from cache, as it should</div><div>    a = np.zeros(1) #no aliasing between inputs<div>    b = np.zeros(1)</div><div>    print foo(a, b)    #should compute and return 0 but instead gets 1 from cache</div><div><br></div><div><br></div><div>Fundamentaly, this is because it turns out that the memory aliasing patterns that arrays may have are lost during pickling. This leads me to two questions:</div><div><br></div><div>1: Is this desirable behavior</div><div>2: is this preventable behavior?</div><div><br></div><div>It seems to me the answer to the first question is no, and the answer to the second question is yes.</div><div><br></div><div>Here is what I am using at the moment to generate a correct hash under such circumstances; but unpickling along these lines should be possible too, methinks. Or am I missing some subtlety as to why something along these lines couldn't be the default pickling behavior for numpy arrays?</div><div><br></div><div><br></div><div>class ndarray_own(object):<br>    def __init__(self, arr):<br>        self.buffer     = np.getbuffer(arr)<br>        self.dtype      = arr.dtype<br>        self.shape      = arr.shape<br>        self.strides    = arr.strides</div><div>class ndarray_view(object):<br>    def __init__(self, arr):<br>        self.base       = arr.base<br>        self.offset     = self.base.ctypes.data - arr.ctypes.data   #so we have a view; but where is it?<br>        self.dtype      = arr.dtype<br>        self.shape      = arr.shape<br>        self.strides    = arr.strides<br></div><div><br></div><div>class NumpyDeterministicPickler(DeterministicPickler):<br>    """<br>    Special case for numpy.<br>    in general, external C objects may include internal state which does not serialize in a way we want it to<br>    ndarray memory aliasing is one of those things<br>    """</div><div><br>    def save(self, obj):<br>        """<br>        remap a numpy array to a representation which conserves<br>        all semantically relevant information concerning memory aliasing<br>        note that this mapping is 'destructive'; we will not get our original numpy arrays<br>        back after unpickling; not without custom deserialization code at least<br>        but we dont care, since this is only meant to be used to obtain correct keying behavior<br>        keys dont need to be deserialized<br>        """<br>        if isinstance(obj, np.ndarray):<br>            if obj.flags.owndata:<br>                obj = ndarray_own(obj)<br>            else:<br>                obj = ndarray_view(obj)<br>        DeterministicPickler.save(self, obj)<br></div></div><div><br></div></div></div>