Inconsistency with __index__() for rank-1 arrays?

Hi, I find this a bit misleading:
a = np.arange(10)
a[np.array(0)] 0
a[np.array([0])] array([0])
a[[0]] array([0])
But, for regular python lists we have:
l = a.tolist()
l[np.array(0)] 0
l[np.array([0])] 0
i.e. indexing with a rank-0 array and a rank-1 array with one single element return the same result, which I find inconsistent with the expected behaviour for this case, i.e.:
l[[0]]
TypeError Traceback (most recent call last) /tmp/tables-2.2/<ipython console> in <module>() TypeError: list indices must be integers, not list The ultimate reason for this behaviour is this:
np.array(0).__index__() 0
np.array([0]).__index__() 0
But I wonder why NumPy needs the latter behaviour, instead of the more logical:
np.array([0]).__index__()
TypeError Traceback (most recent call last) /tmp/tables-2.2/<ipython console> in <module>() TypeError: only rank-0 integer arrays can be converted to an index This inconsistency has indeed introduced a bug in my application and for solving this I'd need something like: """ def is_idx(index): """Check if an object can work as an index or not.""" if hasattr(index, "__index__"): # Only works on Python 2.5 on if (hasattr(index, "shape") and index.shape == (1,)): return False try: # (as per PEP 357) idx = index.__index__() return True except TypeError: return False return False """ i.e. for determining if an object can be an index or not, I need to explicitly check for a shape different from (1,), which is unnecessarily complicated. So I find the current behaviour prone to introduce errors in apps and I'm wondering why exactly np.array([1]) should work as an index at all. It would not be better if that would raise a ``TypeError``? Thanks, -- Francesc Alted

The __index__ method returns an integer from an array. The current behavior follows the idea of "return an integer if there is 1-element in the array" Your suggestion is to only return an integer if it is a rank-0 array, otherwise raise an error. This could potentially be changed in NumPy 2.0. I'm +0 on the suggestion. -Travis On Oct 27, 2010, at 9:34 AM, Francesc Alted wrote:
Hi,
I find this a bit misleading:
a = np.arange(10)
a[np.array(0)] 0
a[np.array([0])] array([0])
a[[0]] array([0])
But, for regular python lists we have:
l = a.tolist()
l[np.array(0)] 0
l[np.array([0])] 0
i.e. indexing with a rank-0 array and a rank-1 array with one single element return the same result, which I find inconsistent with the expected behaviour for this case, i.e.:
l[[0]]
TypeError Traceback (most recent call last)
/tmp/tables-2.2/<ipython console> in <module>()
TypeError: list indices must be integers, not list
The ultimate reason for this behaviour is this:
np.array(0).__index__() 0
np.array([0]).__index__() 0
But I wonder why NumPy needs the latter behaviour, instead of the more logical:
np.array([0]).__index__()
TypeError Traceback (most recent call last)
/tmp/tables-2.2/<ipython console> in <module>()
TypeError: only rank-0 integer arrays can be converted to an index
This inconsistency has indeed introduced a bug in my application and for solving this I'd need something like:
""" def is_idx(index): """Check if an object can work as an index or not."""
if hasattr(index, "__index__"): # Only works on Python 2.5 on if (hasattr(index, "shape") and index.shape == (1,)): return False try: # (as per PEP 357) idx = index.__index__() return True except TypeError: return False
return False """
i.e. for determining if an object can be an index or not, I need to explicitly check for a shape different from (1,), which is unnecessarily complicated.
So I find the current behaviour prone to introduce errors in apps and I'm wondering why exactly np.array([1]) should work as an index at all. It would not be better if that would raise a ``TypeError``?
Thanks,
-- Francesc Alted _______________________________________________ NumPy-Discussion mailing list NumPy-Discussion@scipy.org http://mail.scipy.org/mailman/listinfo/numpy-discussion
--- Travis Oliphant Enthought, Inc. oliphant@enthought.com 1-512-536-1057 http://www.enthought.com

Hi Travis, thanks for answering, A Friday 29 October 2010 06:34:52 Travis Oliphant escrigué:
The __index__ method returns an integer from an array.
The current behavior follows the idea of "return an integer if there is 1-element in the array"
Your suggestion is to only return an integer if it is a rank-0 array, otherwise raise an error.
Yes. I think this makes a lot of sense because a rank-0 array can be seen as a scalar (and hence, and index), but it is difficult to see a rank-1 (or, in general, rank-N) array as a scalar (even if only has 1 single element). In particular, this would avoid this inconsistency:
a[np.array(1)] 1 a[np.array([1])] array([1]) a[np.array([[[1]]])] array([[[1]]])
but:
np.array(1).__index__() 1 np.array([1]).__index__() 1 np.array([[[1]]]).__index__() 1
This could potentially be changed in NumPy 2.0. I'm +0 on the suggestion.
My vote is +1 for deprecating ``array([scalar])`` as a scalar index for NumPy 2.0. -- Francesc Alted

Fri, 29 Oct 2010 09:54:23 +0200, Francesc Alted wrote: [clip]
My vote is +1 for deprecating ``array([scalar])`` as a scalar index for NumPy 2.0.
I'd be -0 on this, since 1-element Numpy arrays function like scalars in several other contexts, e.g. in casting to Python types and in boolean context. Note also that at least Python's struct module (mis-?)uses __index__() for casting inputs to integers. -- Pauli Virtanen

A Friday 29 October 2010 12:18:20 Pauli Virtanen escrigué:
Fri, 29 Oct 2010 09:54:23 +0200, Francesc Alted wrote: [clip]
My vote is +1 for deprecating ``array([scalar])`` as a scalar index for NumPy 2.0.
I'd be -0 on this, since 1-element Numpy arrays function like scalars in several other contexts, e.g. in casting to Python types and in boolean context.
Note also that at least Python's struct module (mis-?)uses __index__() for casting inputs to integers.
Uh, could you put some examples of this please? -- Francesc Alted

pe, 2010-10-29 kello 12:48 +0200, Francesc Alted kirjoitti:
A Friday 29 October 2010 12:18:20 Pauli Virtanen escrigué:
Fri, 29 Oct 2010 09:54:23 +0200, Francesc Alted wrote: [clip]
My vote is +1 for deprecating ``array([scalar])`` as a scalar index for NumPy 2.0.
I'd be -0 on this, since 1-element Numpy arrays function like scalars in several other contexts, e.g. in casting to Python types and in boolean context.
Note also that at least Python's struct module (mis-?)uses __index__() for casting inputs to integers.
Uh, could you put some examples of this please?
Sure: if array([[1]]): print "OK" x = float(array([[1]])) and for the second point, in Python/Modules/_struct.c: 95 static PyObject * 96 get_pylong(PyObject *v) 97 { 98 assert(v != NULL); 99 if (!PyLong_Check(v)) { 100 /* Not an integer; try to use __index__ to convert. */ 101 if (PyIndex_Check(v)) { >>> import numpy as np, struct >>> struct.pack("i", np.array([1])) '\x01\x00\x00\x00' -- Pauli Virtanen

A Friday 29 October 2010 12:59:04 Pauli Virtanen escrigué:
pe, 2010-10-29 kello 12:48 +0200, Francesc Alted kirjoitti:
A Friday 29 October 2010 12:18:20 Pauli Virtanen escrigué:
Fri, 29 Oct 2010 09:54:23 +0200, Francesc Alted wrote: [clip]
My vote is +1 for deprecating ``array([scalar])`` as a scalar index for NumPy 2.0.
I'd be -0 on this, since 1-element Numpy arrays function like scalars in several other contexts, e.g. in casting to Python types and in boolean context.
Note also that at least Python's struct module (mis-?)uses __index__() for casting inputs to integers.
Uh, could you put some examples of this please?
Sure:
if array([[1]]): print "OK"
x = float(array([[1]]))
and for the second point, in Python/Modules/_struct.c:
95 static PyObject * 96 get_pylong(PyObject *v) 97 { 98 assert(v != NULL); 99 if (!PyLong_Check(v)) { 100 /* Not an integer; try to use __index__ to convert. */ 101 if (PyIndex_Check(v)) {
>>> import numpy as np, struct >>> struct.pack("i", np.array([1]))
'\x01\x00\x00\x00'
I see. What I do not see if this behaviour is desirable (in fact, I think it is not, but I may be wrong of course). -- Francesc Alted
participants (3)
-
Francesc Alted
-
Pauli Virtanen
-
Travis Oliphant