Repository: numpy/numpy

Issue:
iscomplexobj does not work for sparse matrices
```
>>> import scipy
>>> import numpy as np
>>> A = scipy.sparse.csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5j]])
>>> np.iscomplexobj(A)
False
```

I would argue that the implementation of `iscomplexobj` should not include the `asarray`, but should simply read

```
return issubclass(x.dtype.type, _nx.complexfloating)
```

This might cause an `AttributeError`, of course. Not sure whether it's better to throw the exception at this point, or whether to handle it locally.



Discussion:
None of the examples in the current docstring have a valid dtype:

```
>>> np.iscomplexobj(1)
False
>>> np.iscomplexobj(1+0j)
True
>>> np.iscomplexobj([3, 1+0j, True])
True
```

But we could certainly check for a `dtype` attribute before calling asarray in the style of existing support for duck array types in NumPy, e.g.,

```
try:
    dtype = x.dtype
except AttributeError:
    dtype = np.asarray(x).dtype
return issubclass(dtype.type, _nx.complexfloating)
```


Resolving patch:
diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py
--- a/numpy/lib/type_check.py
+++ b/numpy/lib/type_check.py
@@ -266,7 +266,15 @@ def iscomplexobj(x):
     True
 
     """
-    return issubclass(asarray(x).dtype.type, _nx.complexfloating)
+    try:
+        dtype = x.dtype
+    except AttributeError:
+        dtype = asarray(x).dtype
+    try:
+        return issubclass(dtype.type, _nx.complexfloating)
+    except AttributeError:
+        return False
+
 
 def isrealobj(x):
     """
@@ -300,7 +308,7 @@ def isrealobj(x):
     False
 
     """
-    return not issubclass(asarray(x).dtype.type, _nx.complexfloating)
+    return not iscomplexobj(x)
 
 #-----------------------------------------------------------------------------
 


Test patch:
diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py
--- a/numpy/lib/tests/test_type_check.py
+++ b/numpy/lib/tests/test_type_check.py
@@ -148,6 +148,41 @@ def test_basic(self):
         z = np.array([-1j, 0, -1])
         assert_(iscomplexobj(z))
 
+    def test_scalar(self):
+        assert_(not iscomplexobj(1.0))
+        assert_(iscomplexobj(1+0j))
+
+    def test_list(self):
+        assert_(iscomplexobj([3, 1+0j, True]))
+        assert_(not iscomplexobj([3, 1, True]))
+
+    def test_duck(self):
+        class DummyComplexArray:
+            @property
+            def dtype(self):
+                return np.dtype(complex)
+        dummy = DummyComplexArray()
+        assert_(iscomplexobj(dummy))
+
+    def test_pandas_duck(self):
+        # This tests a custom np.dtype duck-typed class, such as used by pandas
+        # (pandas.core.dtypes)
+        class PdComplex(np.complex128):
+            pass
+        class PdDtype(object):
+            name = 'category'
+            names = None
+            type = PdComplex
+            kind = 'c'
+            str = '<c16'
+            base = np.dtype('complex128')
+        class DummyPd:
+            @property
+            def dtype(self):
+                return PdDtype
+        dummy = DummyPd()
+        assert_(iscomplexobj(dummy))
+
 
 class TestIsrealobj(TestCase):
     def test_basic(self):