Repository: numpy/numpy

Issue:
np.ma.count and np.ma.copy on list input
The `np.ma.count` and `np.ma.copy` behave strangly if the input is a `list` or `tuple` (probably also other python builtins).

```
>>> np.ma.copy([1,2,3])  # unexpected behaviour
[1, 2, 3]

>>> np.copy([1,2,3])  # expected behaviour
array([1, 2, 3])

>>> np.ma.count([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Programming\Anaconda\envs\test\lib\site-packages\numpy\ma\core.py", line 6389, in __call__
    return method(*args, **params)
TypeError: count() takes exactly one argument (0 given)
```

The reason for this behaviour is that `list` **has** a `count` and `copy` method and `np.ma._frommethod` tries to call it instead of `np.ma.MaskedArray.count` (and copy).



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
@@ -6372,21 +6372,16 @@ def getdoc(self):
     def __call__(self, a, *args, **params):
         if self.reversed:
             args = list(args)
-            arr = args[0]
-            args[0] = a
-            a = arr
-        # Get the method from the array (if possible)
+            a, args[0] = args[0], a
+
+        marr = asanyarray(a)
         method_name = self.__name__
-        method = getattr(a, method_name, None)
-        if method is not None:
-            return method(*args, **params)
-        # Still here ? Then a is not a MaskedArray
-        method = getattr(MaskedArray, method_name, None)
-        if method is not None:
-            return method(MaskedArray(a), *args, **params)
-        # Still here ? OK, let's call the corresponding np function
-        method = getattr(np, method_name)
-        return method(a, *args, **params)
+        method = getattr(type(marr), method_name, None)
+        if method is None:
+            # use the corresponding np function
+            method = getattr(np, method_name)
+
+        return method(marr, *args, **params)
 
 
 all = _frommethod('all')
@@ -6535,9 +6530,7 @@ def compressed(x):
         Equivalent method.
 
     """
-    if not isinstance(x, MaskedArray):
-        x = asanyarray(x)
-    return x.compressed()
+    return asanyarray(x).compressed()
 
 
 def concatenate(arrays, axis=0):
@@ -7683,6 +7676,10 @@ def asanyarray(a, dtype=None):
     <class 'numpy.ma.core.MaskedArray'>
 
     """
+    # workaround for #8666, to preserve identity. Ideally the bottom line
+    # would handle this for us.
+    if isinstance(a, MaskedArray) and (dtype is None or dtype == a.dtype):
+        return a
     return masked_array(a, dtype=dtype, copy=False, keep_mask=True, subok=True)
 
 


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
@@ -433,6 +433,11 @@ def test_copy(self):
         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_on_python_builtins(self):
+        # Tests copy works on python builtins (issue#8019)
+        self.assertTrue(isMaskedArray(np.ma.copy([1,2,3])))
+        self.assertTrue(isMaskedArray(np.ma.copy((1,2,3))))
+
     def test_copy_immutable(self):
         # Tests that the copy method is immutable, GitHub issue #5247
         a = np.ma.array([1, 2, 3])
@@ -1032,6 +1037,11 @@ def test_count_func(self):
         assert_(res.dtype.type is np.intp)
         assert_raises(np.AxisError, ott.count, axis=1)
 
+    def test_count_on_python_builtins(self):
+        # Tests count works on python builtins (issue#8019)
+        assert_equal(3, count([1,2,3]))
+        assert_equal(2, count((1,2)))
+
     def test_minmax_func(self):
         # Tests minimum and maximum.
         (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d