Retrieve length of slice from slice object in Python

后端 未结 5 1217
灰色年华
灰色年华 2021-01-04 03:05

The title explains itself, how to get 2 out of the object

slice(0,2)

The documentation is somewhat confusing, or it is the wrong one

<
5条回答
  •  情话喂你
    2021-01-04 03:33

    So it looks like slice.indices(n) returns the arguments to be given to range, to get the item indices which should be reflected in the slice of a sequence of length n (although it's not documented edit: as @ShadowRanger pointed out, it is indeed documented). So the following lines evaluate to the same values:

    # get some list to work on
    my_list = list(range(100))
    
    # slice syntax
    print(my_list[1:15:3])
    # regular item access
    print(my_list[slice(1,15,3)])
    # reinvent list slicing
    print([my_list[i] for i in range(*slice(1,15,3).indices(len(my_list)))])
    

    As you see, the resulting list's length is the same as the length of range(*slice(1,15,3).indices(len(my_list))), which depends on the slice object itself, and the length of the sequence to be sliced. That's why len(range(*slice.indices(n))) will give you the right answer in Python 3. (the range object is a generator, which fortunately has the __len__ function defined, so it can give you the item count, without the need to enumerate and count them.)

    If you work with large numbers in python 2, you can replicate the calculation as @ShadowRanger suggests.

    The original implementation of range.__len__ is the following:

    /* Return number of items in range (lo, hi, step).  step != 0
     * required.  The result always fits in an unsigned long.
     */
    static unsigned long
    get_len_of_range(long lo, long hi, long step)
    {
        /* -------------------------------------------------------------
        If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty.
        Else for step > 0, if n values are in the range, the last one is
        lo + (n-1)*step, which must be <= hi-1.  Rearranging,
        n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
        the proper value.  Since lo < hi in this case, hi-lo-1 >= 0, so
        the RHS is non-negative and so truncation is the same as the
        floor.  Letting M be the largest positive long, the worst case
        for the RHS numerator is hi=M, lo=-M-1, and then
        hi-lo-1 = M-(-M-1)-1 = 2*M.  Therefore unsigned long has enough
        precision to compute the RHS exactly.  The analysis for step < 0
        is similar.
        ---------------------------------------------------------------*/
        assert(step != 0);
        if (step > 0 && lo < hi)
        return 1UL + (hi - 1UL - lo) / step;
        else if (step < 0 && lo > hi)
        return 1UL + (lo - 1UL - hi) / (0UL - step);
        else
        return 0UL;
    }
    

    And slice.indices:

    int
    PySlice_GetIndices(PySliceObject *r, Py_ssize_t length,
                       Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step)
    {
        /* XXX support long ints */
        if (r->step == Py_None) {
            *step = 1;
        } else {
            if (!PyInt_Check(r->step) && !PyLong_Check(r->step)) return -1;
            *step = PyInt_AsSsize_t(r->step);
        }
        if (r->start == Py_None) {
            *start = *step < 0 ? length-1 : 0;
        } else {
            if (!PyInt_Check(r->start) && !PyLong_Check(r->step)) return -1;
            *start = PyInt_AsSsize_t(r->start);
            if (*start < 0) *start += length;
        }
        if (r->stop == Py_None) {
            *stop = *step < 0 ? -1 : length;
        } else {
            if (!PyInt_Check(r->stop) && !PyLong_Check(r->step)) return -1;
            *stop = PyInt_AsSsize_t(r->stop);
            if (*stop < 0) *stop += length;
        }
        if (*stop > length) return -1;
        if (*start >= length) return -1;
        if (*step == 0) return -1;
        return 0;
    }
    

    The sources are from svn

提交回复
热议问题