summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/1.12.0-notes.rst7
-rw-r--r--numpy/core/memmap.py21
-rw-r--r--numpy/core/tests/test_memmap.py49
3 files changed, 76 insertions, 1 deletions
diff --git a/doc/release/1.12.0-notes.rst b/doc/release/1.12.0-notes.rst
index 486759494..b9e405154 100644
--- a/doc/release/1.12.0-notes.rst
+++ b/doc/release/1.12.0-notes.rst
@@ -181,6 +181,13 @@ were doing. This caused a complication in the downstream 'pandas' library
that encountered an issue with 'numpy' compatibility. Now, all array-like
methods in this module are called with keyword arguments instead.
+Operations on np.memmap objects return numpy arrays in most cases
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Previously operations on a memmap (e.g. adding 1 to it) object would
+misleadingly return a memmap instance even if the result was actually
+not memmapped. Also reduction of a memmap (e.g. ``.sum(axis=None``)
+return a numpy scalar instead of a 0d memmap.
+
Deprecations
============
diff --git a/numpy/core/memmap.py b/numpy/core/memmap.py
index 70d7b72b4..827909c47 100644
--- a/numpy/core/memmap.py
+++ b/numpy/core/memmap.py
@@ -309,3 +309,24 @@ class memmap(ndarray):
"""
if self.base is not None and hasattr(self.base, 'flush'):
self.base.flush()
+
+ def __array_wrap__(self, arr, context=None):
+ arr = super(memmap, self).__array_wrap__(arr, context)
+
+ # Return a memmap if a memmap was given as the output of the
+ # ufunc. Leave the arr class unchanged if self is not a memmap
+ # to keep original memmap subclasses behavior
+ if self is arr or type(self) is not memmap:
+ return arr
+ # Return scalar instead of 0d memmap, e.g. for np.sum with
+ # axis=None
+ if arr.shape == ():
+ return arr[()]
+ # Return ndarray otherwise
+ return arr.view(np.ndarray)
+
+ def __getitem__(self, index):
+ res = super(memmap, self).__getitem__(index)
+ if type(res) is memmap and res._mmap is None:
+ return res.view(type=ndarray)
+ return res
diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py
index e41758c51..47f58ea7e 100644
--- a/numpy/core/tests/test_memmap.py
+++ b/numpy/core/tests/test_memmap.py
@@ -5,7 +5,9 @@ import os
import shutil
from tempfile import NamedTemporaryFile, TemporaryFile, mktemp, mkdtemp
-from numpy import memmap
+from numpy import (
+ memmap, sum, average, product, ndarray, isscalar, add, subtract, multiply)
+
from numpy import arange, allclose, asarray
from numpy.testing import (
TestCase, run_module_suite, assert_, assert_equal, assert_array_equal,
@@ -126,5 +128,50 @@ class TestMemmap(TestCase):
new_array = asarray(fp)
assert_(new_array.base is fp)
+ def test_ufunc_return_ndarray(self):
+ fp = memmap(self.tmpfp, dtype=self.dtype, shape=self.shape)
+ fp[:] = self.data
+
+ for unary_op in [sum, average, product]:
+ result = unary_op(fp)
+ assert_(isscalar(result))
+ assert_(result.__class__ is self.data[0, 0].__class__)
+
+ assert_(unary_op(fp, axis=0).__class__ is ndarray)
+ assert_(unary_op(fp, axis=1).__class__ is ndarray)
+
+ for binary_op in [add, subtract, multiply]:
+ assert_(binary_op(fp, self.data).__class__ is ndarray)
+ assert_(binary_op(self.data, fp).__class__ is ndarray)
+ assert_(binary_op(fp, fp).__class__ is ndarray)
+
+ fp += 1
+ assert(fp.__class__ is memmap)
+ add(fp, 1, out=fp)
+ assert(fp.__class__ is memmap)
+
+ def test_getitem(self):
+ fp = memmap(self.tmpfp, dtype=self.dtype, shape=self.shape)
+ fp[:] = self.data
+
+ assert_(fp[1:, :-1].__class__ is memmap)
+ # Fancy indexing returns a copy that is not memmapped
+ assert_(fp[[0, 1]].__class__ is ndarray)
+
+ def test_memmap_subclass(self):
+ class MemmapSubClass(memmap):
+ pass
+
+ fp = MemmapSubClass(self.tmpfp, dtype=self.dtype, shape=self.shape)
+ fp[:] = self.data
+
+ # We keep previous behavior for subclasses of memmap, i.e. the
+ # ufunc and __getitem__ output is never turned into a ndarray
+ assert_(sum(fp, axis=0).__class__ is MemmapSubClass)
+ assert_(sum(fp).__class__ is MemmapSubClass)
+ assert_(fp[1:, :-1].__class__ is MemmapSubClass)
+ assert(fp[[0, 1]].__class__ is MemmapSubClass)
+
+
if __name__ == "__main__":
run_module_suite()