summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Helmus <jjhelmus@gmail.com>2015-10-29 20:53:26 -0500
committerJonathan Helmus <jjhelmus@gmail.com>2015-10-29 20:53:26 -0500
commitdb85edeb9326d4ea736e41baf23ae8edfdd6f84c (patch)
tree5ea7bb3fd8214912e52e6c56de5770630e3c850d
parentb2748c6586549f9ee8ddfefcf2a78f2f41c55783 (diff)
downloadnumpy-db85edeb9326d4ea736e41baf23ae8edfdd6f84c.tar.gz
BUG: immutable _arraymethod function in ma.core
Replace the _arraymethod class in ma.core with a function factory which returns class method wrappers around basic array methods. These methods are bound to the MaskedArray instance and are immutable. Previously _arraymethod was a class which would incorrectly operate on the MaskedArray object which last accessed the particular named function. closes #5247
-rw-r--r--numpy/ma/core.py67
-rw-r--r--numpy/ma/tests/test_core.py8
2 files changed, 30 insertions, 45 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index 4ea52d0ab..7d9acbd1c 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -2467,25 +2467,18 @@ def flatten_structured_array(a):
return out
-class _arraymethod(object):
+def _arraymethod(funcname, onmask=True):
"""
- Define a wrapper for basic array methods.
+ Return a class method wrapper around a basic array method.
- Upon call, returns a masked array, where the new ``_data`` array is
- the output of the corresponding method called on the original
- ``_data``.
+ Creates a class method which returns a masked array, where the new
+ ``_data`` array is the output of the corresponding basic method called
+ on the original ``_data``.
If `onmask` is True, the new mask is the output of the method called
on the initial mask. Otherwise, the new mask is just a reference
to the initial mask.
- Attributes
- ----------
- _onmask : bool
- Holds the `onmask` parameter.
- obj : object
- The object calling `_arraymethod`.
-
Parameters
----------
funcname : str
@@ -2495,47 +2488,31 @@ class _arraymethod(object):
alone (False). Default is True. Make available as `_onmask`
attribute.
- """
-
- def __init__(self, funcname, onmask=True):
- self.__name__ = funcname
- self._onmask = onmask
- self.obj = None
- self.__doc__ = self.getdoc()
-
- def getdoc(self):
- "Return the doc of the function (from the doc of the method)."
- methdoc = getattr(ndarray, self.__name__, None) or \
- getattr(np, self.__name__, None)
- if methdoc is not None:
- return methdoc.__doc__
-
- def __get__(self, obj, objtype=None):
- self.obj = obj
- return self
+ Returns
+ -------
+ method : instancemethod
+ Class method wrapper of the specified basic array method.
- def __call__(self, *args, **params):
- methodname = self.__name__
- instance = self.obj
- # Fallback : if the instance has not been initialized, use the first
- # arg
- if instance is None:
- args = list(args)
- instance = args.pop(0)
- data = instance._data
- mask = instance._mask
- cls = type(instance)
- result = getattr(data, methodname)(*args, **params).view(cls)
- result._update_from(instance)
+ """
+ def wrapped_method(self, *args, **params):
+ result = getattr(self._data, funcname)(*args, **params)
+ result = result.view(type(self))
+ result._update_from(self)
+ mask = self._mask
if result.ndim:
- if not self._onmask:
+ if not onmask:
result.__setmask__(mask)
elif mask is not nomask:
- result.__setmask__(getattr(mask, methodname)(*args, **params))
+ result.__setmask__(getattr(mask, funcname)(*args, **params))
else:
if mask.ndim and (not mask.dtype.names and mask.all()):
return masked
return result
+ methdoc = getattr(ndarray, funcname, None) or getattr(np, funcname, None)
+ if methdoc is not None:
+ wrapped_method.__doc__ = methdoc.__doc__
+ wrapped_method.__name__ = funcname
+ return wrapped_method
class MaskedIterator(object):
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index 0a9821254..70c1ee12c 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -401,6 +401,14 @@ class TestMaskedArray(TestCase):
assert_not_equal(y._data.ctypes.data, x._data.ctypes.data)
assert_not_equal(y._mask.ctypes.data, x._mask.ctypes.data)
+ def test_copy_immutable(self):
+ # Tests that the copy method is immutable, GitHub issue #5247
+ a = np.ma.array([1, 2, 3])
+ b = np.ma.array([4, 5, 6])
+ a_copy_method = a.copy
+ b.copy
+ assert_equal(a_copy_method(), [1, 2, 3])
+
def test_deepcopy(self):
from copy import deepcopy
a = array([0, 1, 2], mask=[False, True, False])