Search code examples
pythonnumpy

numpy: formal definition of "array_like" objects?


In numpy, the constructors of many objects accept an "array_like" as first argument. Is there a definition of a such object, either as an abstract meta class, or documentation of the methods is should contain??


Solution

  • It turns out almost anything is technically an array-like. "Array-like" is more of a statement of how the input will be interpreted than a restriction on what the input can be; if a parameter is documented as array-like, NumPy will try to interpret it as an array.

    There is no formal definition of array-like beyond the nearly tautological one -- an array-like is any Python object that np.array can convert to an ndarray. To go beyond this, you'd need to study the source code.

    NPY_NO_EXPORT PyObject *
    PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
                    int max_depth, int flags, PyObject *context)
    {
        /*
         * This is the main code to make a NumPy array from a Python
         * Object.  It is called from many different places.
         */
        PyArrayObject *arr = NULL, *ret;
        PyArray_Descr *dtype = NULL;
        int ndim = 0;
        npy_intp dims[NPY_MAXDIMS];
    
        /* Get either the array or its parameters if it isn't an array */
        if (PyArray_GetArrayParamsFromObject(op, newtype,
                            0, &dtype,
                            &ndim, dims, &arr, context) < 0) {
            Py_XDECREF(newtype);
            return NULL;
        }
        ...
    

    Particularly interesting is PyArray_GetArrayParamsFromObject, whose comments enumerate the types of objects np.array expects:

    NPY_NO_EXPORT int
    PyArray_GetArrayParamsFromObject(PyObject *op,
                            PyArray_Descr *requested_dtype,
                            npy_bool writeable,
                            PyArray_Descr **out_dtype,
                            int *out_ndim, npy_intp *out_dims,
                            PyArrayObject **out_arr, PyObject *context)
    {
        PyObject *tmp;
    
        /* If op is an array */
    
        /* If op is a NumPy scalar */
    
        /* If op is a Python scalar */
    
        /* If op supports the PEP 3118 buffer interface */
    
        /* If op supports the __array_struct__ or __array_interface__ interface */
    
        /*
         * If op supplies the __array__ function.
         * The documentation says this should produce a copy, so
         * we skip this method if writeable is true, because the intent
         * of writeable is to modify the operand.
         * XXX: If the implementation is wrong, and/or if actual
         *      usage requires this behave differently,
         *      this should be changed!
         */
    
        /* Try to treat op as a list of lists */
    
        /* Anything can be viewed as an object, unless it needs to be writeable */
    
    }
    

    So by studying the source code we can conclude an array-like is