[Numpy-discussion] numpy oddity

Robert Kern robert.kern at gmail.com
Tue Aug 30 14:17:35 EDT 2011


On Tue, Aug 30, 2011 at 09:52, Charles R Harris
<charlesr.harris at gmail.com> wrote:
>
> On Tue, Aug 30, 2011 at 8:33 AM, Johann Cohen-Tanugi
> <johann.cohentanugi at gmail.com> wrote:
>>
>> I have numpy version 1.6.1 and I see the following behavior :
>>
>> In [380]: X
>> Out[380]: 1.0476157527896641
>>
>> In [381]: X.__class__
>> Out[381]: numpy.float64
>>
>> In [382]: (2,3)*X
>> Out[382]: (2, 3)
>>
>> In [383]: (2,3)/X
>> Out[383]: array([ 1.90909691,  2.86364537])
>>
>> In [384]: X=float(X)
>>
>> In [385]: (2,3)/X
>>
>> ---------------------------------------------------------------------------
>> TypeError                                 Traceback (most recent call
>> last)
>> /home/cohen/<ipython-input-385-cafbe080bfd5> in <module>()
>> ----> 1 (2,3)/X
>>
>> TypeError: unsupported operand type(s) for /: 'tuple' and 'float'
>>
>>
>> So it appears that X being a numpy float allows numpy to play some trick
>> on the tuple so that division becomes possible, which regular built-in
>> float does not allow arithmetics with tuples.
>> But why is multiplication with "*" not following the same prescription?
>>
>
> That's strange.
>
> In [16]: x = float64(2.1)
>
> In [17]: (2,3)*x
> Out[17]: (2, 3, 2, 3)
>
> In [18]: (2,3)/x
> Out[18]: array([ 0.95238095,  1.42857143])
>
> Note that in the first case x is treated like an integer. In the second the
> tuple is turned into an array. I think both of these cases should raise
> exceptions.

In scalartypes.c.src:

tatic PyObject *
gentype_multiply(PyObject *m1, PyObject *m2)
{
    PyObject *ret = NULL;
    long repeat;

    if (!PyArray_IsScalar(m1, Generic) &&
            ((Py_TYPE(m1)->tp_as_number == NULL) ||
             (Py_TYPE(m1)->tp_as_number->nb_multiply == NULL))) {
        /* Try to convert m2 to an int and try sequence repeat */
        repeat = PyInt_AsLong(m2);
        if (repeat == -1 && PyErr_Occurred()) {
            return NULL;
        }
        ret = PySequence_Repeat(m1, (int) repeat);
    }
    else if (!PyArray_IsScalar(m2, Generic) &&
            ((Py_TYPE(m2)->tp_as_number == NULL) ||
             (Py_TYPE(m2)->tp_as_number->nb_multiply == NULL))) {
        /* Try to convert m1 to an int and try sequence repeat */
        repeat = PyInt_AsLong(m1);
        if (repeat == -1 && PyErr_Occurred()) {
            return NULL;
        }
        ret = PySequence_Repeat(m2, (int) repeat);
    }
    if (ret == NULL) {
        PyErr_Clear(); /* no effect if not set */
        ret = PyArray_Type.tp_as_number->nb_multiply(m1, m2);
    }
    return ret;
}

The PyInt_AsLong() calls should be changed to check for
__index__ability, instead. Not sure about the other operators. Some
people *may* be relying on the coerce-sequences-to-ndarray behavior
with numpy scalars just like they do so with ndarrays. On the other
hand, the repeat behavior with * should have thrown a monkey wrench to
them if they were, so the number of people who do this is probably
small.

-- 
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless
enigma that is made terrible by our own mad attempt to interpret it as
though it had an underlying truth."
  -- Umberto Eco



More information about the NumPy-Discussion mailing list