在Python中从切片对象检索切片的长度

lat*_*ian 8 python list slice

标题说明了自己,如何从对象中取出2

slice(0,2)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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

a = [1,2,3,4,5]
len(a[slice(0,2)])  # 2
Run Code Online (Sandbox Code Playgroud)

但这对于任意大的切片都会失败。

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

Sha*_*ger 6

没有完整的答案。slice不会给出长度,因为结果的长度始终取决于要切片的序列的大小,较短的序列(包括空序列)将产生较少的项,并且如果slice无限,则长度将增加与序列的长度一致;slice通过使用startstop,a 可能只是“到达序列末尾” None

为了快速简便地计算已知长度序列的长度,您可以将其.indices与Py3组合range(或xrange在Py2中组合,尽管xrange对Py3 range没有限制)。slice.indices为您提供具体的值startstop以及stride当将a slice应用于给定长度的序列时派生的值,基本上是您要在C样式for循环中填写的值,它们遍历与相同的索引slice

 for (ssize_t i = start; i < stop; i += stride)
Run Code Online (Sandbox Code Playgroud)

因此,要计算slice应用于1000个元素的序列时的长度,您需要执行以下操作:

>>> len(range(*slice(0, 2).indices(1000)))
2
>>> len(range(*slice(10, None, 3).indices(1000)))
330
Run Code Online (Sandbox Code Playgroud)

如果您使用的是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
Run Code Online (Sandbox Code Playgroud)

更新:不幸的是,slice.indices它本身不会接受len超出a long可以容纳的范围的序列,因此这不会为您xrange在Py2中使用带来任何好处。留给感兴趣的人使用,但是解决方法不会解决任何问题,除非您还执行该工作sliceNone根据序列长度将负值转换为具体值。叹。


Tam*_*dus 5

因此,它看起来像是slice.indices(n)返回要提供给的参数range,以获取应反映在长度序列切片中的项目索引n尽管它没有记录在案 编辑:正如@ShadowRanger 指出的那样,它确实被记录在案)。因此,以下几行评估为相同的值:

# 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)))])
Run Code Online (Sandbox Code Playgroud)

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

如果您在 python 2 中处理大量数字,则可以按照@ShadowRanger 的建议复制计算。

的原始实现range.__len__如下:

# 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)))])
Run Code Online (Sandbox Code Playgroud)

并且slice.indices

/* 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;
}
Run Code Online (Sandbox Code Playgroud)

来源来自svn