在Python中从slice对象检索slice的长度

2024-05-17 06:58:54 发布

您现在位置:Python中文网/ 问答频道 /正文

标题解释了如何从对象中获取2

slice(0,2)

文档有些混乱,或者是错误的

https://docs.python.org/2/c-api/slice.html

尤其是我不明白

slice(0,2).indices(0)  # (0, 0, 1)
slice(0,2).indices(10 ** 10)  # (0, 2, 1)

一种可能的解决方法是使用slice对象对列表进行切片

a = [1,2,3,4,5]
len(a[slice(0,2)])  # 2

但对于任意大的一块来说,这将失败。

谢谢,我在其他帖子里找不到答案。


Tags: 对象方法文档httpsorgapi标题docs
3条回答
>>> slice(0,2).__getattribute__('stop')
2
>>> slice(0,2).__getattribute__('start')
0

对此没有完整的答案。slice不提供长度,因为结果的长度始终取决于被切片序列的大小,短序列(包括空序列)将生成较少的项,如果slice是无限的,则长度将随着序列的长度而增长;一个slice可能只是通过一个startstopNone到达序列的末尾。

为了快速简便地计算已知长度序列的长度,只需将.indices与Py3的range(或Py2中的xrange组合,尽管xrange对Py3的值有限制,而Py3的值没有限制)。^{} gives you the concrete ^{}, ^{} and ^{} values derived when a ^{} applies to a sequence of a given length,基本上是您在C样式的for循环中填写的值,该循环与slice循环遍历相同的索引:

 for (ssize_t i = start; i < stop; i += stride)

因此,要计算当应用于包含1000个元素的序列时slice的长度,您需要:

>>> len(range(*slice(0, 2).indices(1000)))
2
>>> len(range(*slice(10, None, 3).indices(1000)))
330

如果您使用的是Python 2,并且您的值可能超过xrange可以处理的值(它仅限于ssize_t可以容纳的边界和总长度),那么您只需手动进行计算:

def slice_len_for(slc, seqlen):
    start, stop, step = slc.indices(seqlen)
    return max(0, (stop - start + (step - (1 if step > 0 else -1))) // step)

>>> slice_len_for(slice(10, None, 3), 1000)
330

更新:不幸的是,slice.indices本身不会接受超过long所能容纳的序列的len,因此这不会使您在Py2中使用xrange获得任何好处。为那些感兴趣的人留下了位置,但是解决方法不会解决任何问题,除非你也perform the work ^{} does to convert negative values and ^{} to concrete values based on the sequence length.叹息。

所以看起来slice.indices(n)返回要给range的参数,以获得应该反映在长度序列n的切片中的项索引(尽管没有文档化的编辑:正如@ShadowRanger指出的,它确实是documented)。因此,以下几行的计算值相同:

# 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)))])

如您所见,结果列表的长度与range(*slice(1,15,3).indices(len(my_list)))的长度相同,这取决于slice对象本身以及要切片的序列的长度。这就是为什么len(range(*slice.indices(n)))将在Python 3中给出正确的答案。(range对象是一个生成器,幸运的是它定义了__len__函数,因此它可以提供项目计数,而无需枚举和计数它们。)

如果在python 2中使用大数,可以按照@ShadowRanger的建议复制计算。

range.__len__的原始实现如下:

/* 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;
}

以及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;
}

来源于svn

相关问题 更多 >