Repository: numpy/numpy

Issue:
`numpy.where` and `numpy.ma.where` are inconsistent when using `None` as a scalar
Two pull requests have been offered ( https://github.com/numpy/numpy/pull/5582 ) ( https://github.com/numpy/numpy/pull/5583 ) to address the inconsistency. Below are four examples.

```
>>> import numpy
>>> numpy.where(True)
... (array([0]),)
>>> numpy.where(True, None, None)
... array(None, dtype=object)
>>> numpy.ma.where(True)
... (array([0]),)
>>> numpy.ma.where(True, None, None)
... (array([0]),)
```

It it worth noting that it doesn't matter if the first argument (the conditional) is an array or scalar. The inconsistency regards the use of `None` as a scalar, which is currently permitted by `numpy.where`, but not by `numpy.ma.where`.



Discussion:

Resolving patch:
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -29,7 +29,7 @@
 import numpy as np
 import numpy.core.umath as umath
 import numpy.core.numerictypes as ntypes
-from numpy import ndarray, amax, amin, iscomplexobj, bool_
+from numpy import ndarray, amax, amin, iscomplexobj, bool_, _NoValue
 from numpy import array as narray
 from numpy.lib.function_base import angle
 from numpy.compat import getargspec, formatargspec, long, basestring
@@ -6708,7 +6708,7 @@ def size(obj, axis=None):
 #####--------------------------------------------------------------------------
 #---- --- Extra functions ---
 #####--------------------------------------------------------------------------
-def where (condition, x=None, y=None):
+def where(condition, x=_NoValue, y=_NoValue):
     """
     Return a masked array with elements from x or y, depending on condition.
 
@@ -6755,14 +6755,20 @@ def where (condition, x=None, y=None):
      [6.0 -- 8.0]]
 
     """
-    if x is None and y is None:
+    missing = (x is _NoValue, y is _NoValue).count(True)
+
+    if missing == 1:
+        raise ValueError("Must provide both 'x' and 'y' or neither.")
+    if missing == 2:
         return filled(condition, 0).nonzero()
-    elif x is None or y is None:
-        raise ValueError("Either both or neither x and y should be given.")
-    # Get the condition ...............
+
+    # Both x and y are provided
+
+    # Get the condition
     fc = filled(condition, 0).astype(MaskType)
     notfc = np.logical_not(fc)
-    # Get the data ......................................
+
+    # Get the data
     xv = getdata(x)
     yv = getdata(y)
     if x is masked:
@@ -6771,20 +6777,24 @@ def where (condition, x=None, y=None):
         ndtype = xv.dtype
     else:
         ndtype = np.find_common_type([xv.dtype, yv.dtype], [])
+
     # Construct an empty array and fill it
     d = np.empty(fc.shape, dtype=ndtype).view(MaskedArray)
-    _data = d._data
-    np.copyto(_data, xv.astype(ndtype), where=fc)
-    np.copyto(_data, yv.astype(ndtype), where=notfc)
+    np.copyto(d._data, xv.astype(ndtype), where=fc)
+    np.copyto(d._data, yv.astype(ndtype), where=notfc)
+
     # Create an empty mask and fill it
-    _mask = d._mask = np.zeros(fc.shape, dtype=MaskType)
-    np.copyto(_mask, getmask(x), where=fc)
-    np.copyto(_mask, getmask(y), where=notfc)
-    _mask |= getmaskarray(condition)
-    if not _mask.any():
-        d._mask = nomask
+    mask = np.zeros(fc.shape, dtype=MaskType)
+    np.copyto(mask, getmask(x), where=fc)
+    np.copyto(mask, getmask(y), where=notfc)
+    mask |= getmaskarray(condition)
+
+    # Use d._mask instead of d.mask to avoid copies
+    d._mask = mask if mask.any() else nomask
+
     return d
 
+
 def choose (indices, choices, out=None, mode='raise'):
     """
     Use an index array to construct a new array from a set of choices.


Test patch:
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -3396,6 +3396,13 @@ def test_where(self):
         assert_equal(d, [-9, -9, -9, -9, -9, 4, -9, -9, 10, -9, -9, 3])
         assert_equal(d.dtype, ixm.dtype)
 
+    def test_where_object(self):
+        a = np.array(None)
+        b = masked_array(None)
+        r = b.copy()
+        assert_equal(np.ma.where(True, a, a), r)
+        assert_equal(np.ma.where(True, b, b), r)
+
     def test_where_with_masked_choice(self):
         x = arange(10)
         x[3] = masked