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

在Python中从切片对象检索切片长度,python,list,slice,Python,List,Slice,标题解释了如何从对象中提取2 slice(0,2) 文档有点混乱,或者是错误的 特别是我不明白输出的含义是什么 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 但对于任意大的切片,这将失败 谢谢,我在其他帖子中找不到答案 对此没有完整的答案切片不会给出

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

slice(0,2)
文档有点混乱,或者是错误的

特别是我不明白输出的含义是什么

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
但对于任意大的切片,这将失败


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

对此没有完整的答案<代码>切片不会给出长度,因为结果的长度始终取决于被切片的序列的大小,短序列(包括空序列)将生成较少的项,如果
切片
是无界的,则长度将随序列的长度一起增长;一个
切片
可以通过使用
开始
停止
转到“序列的结尾”

为了快速简便地计算已知长度序列的长度,只需将
.index
与Py3的
范围
(或Py2中的
xrange
,尽管
xrange
对Py3
范围
没有的值有限制),它基本上是您在C样式的
for
循环中填充的值,该循环遍历与
切片相同的索引:

 for (ssize_t i = start; i < stop; i += stride)
如果您使用的是Python2,并且您的值可能超过
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.indexes
本身不接受序列的
len
,超出了
long
所能容纳的范围,因此在Py2中使用
xrange
并没有任何好处。保留给感兴趣的人,但除非您也叹息,否则解决方法不会解决任何问题。

因此它看起来像是
切片。索引(n)
返回给
范围的参数,以获得应反映在长度
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)))])
如您所见,结果列表的长度与
range(*slice(1,15,3)的长度相同。索引(len(my_list))
,这取决于
slice
对象本身和要切片的序列的长度。这就是为什么
len(range(*slice.indexs(n))
将在Python 3中给出正确答案的原因。(range对象是一个生成器,幸运的是它定义了
\uuu len\uuu
函数,因此它可以为您提供项目计数,而无需枚举和计数。)

如果在Python2中使用大量数字,可以按照@ShadowRanger的建议复制计算

范围的原始实现如下所示:

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

一种简化的资产方法 长度取决于被切片的目标对象。 但可以定义最大长度

示例

像这样定义最大长度函数

def slice_len_max(s):
    assert (s.start is not None)
    assert (s.stop is not None)
    step = 1
    if s.step is not None:
        step = s.step
    return max((s.stop - s.start) // step, 1)
并检查输出

>>> slice_len_max(slice(0, 10))
10
>>> slice_len_max(slice(0, 10, 2))
5
>>> slice_len_max(slice(0, 10, 3))
3
>>> slice_len_max(slice(0, 10, 10))
1
>>> slice_len_max(slice(0, 10, 100))
1
>>> slice_len_max(slice(3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in slice_len_max
AssertionError
>>切片长度最大值(切片(0,10))
10
>>>切片长度最大值(切片(0,10,2))
5.
>>>切片长度最大值(切片(0,10,3))
3.
>>>切片长度最大值(切片(0、10、10))
1.
>>>切片长度最大值(切片(0、10、100))
1.
>>>切片长度最大值(切片(3))
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第2行,切片长度最大
断言错误
最后一个调用崩溃,因为切片没有定义
start
属性。

如果序列的长度已知 ShadowRanger的回答涵盖了一般的解决方案,但是如果(像我一样)你知道序列的长度,那么这里有一个简单的方法,可以像
range
那样处理它(包括大多数边缘情况),并且不需要对潜在的长序列进行迭代

这与Markus编写的类似,但处理的边缘情况更多

from math import ceil

def max_slice_len(s: slice):
assert s.stop or s.stop == 0, "Must define stop for max slice len!"
assert s.step != 0, "Step slice cannot be zero"

start = s.start or 0
stop = s.stop
step = s.step or 1

delta = (stop - start)
dsteps = int(ceil(delta / step))

return dsteps if dsteps >= 0 else 0


def slice_len(s: slice, src_len: int):
    stop = min(s.stop, src_len)
    return max_slice_len(slice(s.start, stop, s.step))
说明: 假设我们可以得到一个切片的“最大长度”,而不需要一些
src\u len
, 然后,我们可以将
src_len
(列表的长度或您想要迭代的任何内容)作为切片的
stop
(如果它小于当前的
stop

但这仍然存在找到“最大长度”的问题


获取切片的最大长度 python中的切片构造创建了一种 其中
a0
=
start
d
=
step
n
=
len

一个公式告诉我们:
a_n=a0+(n-1)d

[
a\u n
是序列的第n个元素]
如果我们将
stop
视为
a\u n
,那么:
stop=start+(len-1)*步骤

重新安排我们得到:
len=[(停止-开始)/step]+1

这很好地解决了我们的反向迭代问题(即[10:0:-1])

但它通常会返回一个浮动,因为停止可能不是开始之后的完整“步数”(即对于[0:10:3],(10-0)/3给出了3.3333…。
使用
ceil
可以解决这个问题

剩下的唯一问题是负面结果([10:0:1]将给出(0-10)/1=-10),但实际的“长度”应为零。
解决方案是,如果dsteps>=0,则返回
dsteps,否则返回0


测验
仅供参考,.交互式解释器中的docstring也很有用。@ShadowRanger哇,你在这一点上也是对的!我没能找到。(如果你不介意的话,我用这些信息更新了我的答案)补充说明(为亡灵巫术道歉):“range对象是一个生成器,幸运的是它定义了
\u len\u
函数”是错误的,它不是发电机,它实际上是一个完整的序列
def slice_len_max(s):
    assert (s.start is not None)
    assert (s.stop is not None)
    step = 1
    if s.step is not None:
        step = s.step
    return max((s.stop - s.start) // step, 1)
>>> slice_len_max(slice(0, 10))
10
>>> slice_len_max(slice(0, 10, 2))
5
>>> slice_len_max(slice(0, 10, 3))
3
>>> slice_len_max(slice(0, 10, 10))
1
>>> slice_len_max(slice(0, 10, 100))
1
>>> slice_len_max(slice(3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in slice_len_max
AssertionError
from math import ceil

def max_slice_len(s: slice):
assert s.stop or s.stop == 0, "Must define stop for max slice len!"
assert s.step != 0, "Step slice cannot be zero"

start = s.start or 0
stop = s.stop
step = s.step or 1

delta = (stop - start)
dsteps = int(ceil(delta / step))

return dsteps if dsteps >= 0 else 0


def slice_len(s: slice, src_len: int):
    stop = min(s.stop, src_len)
    return max_slice_len(slice(s.start, stop, s.step))
import unittest
# import max_slice_len, slice_len

class TestSliceUtil(unittest.TestCase):
    def test_max_len_suite(self):
        simple_test_cases = [
            (slice(0, 10, 1), 10),
            (slice(0, 10, 2), 5),
            (slice(0, 10, 3), 4),
            (slice(0, 10, 10), 1),
            (slice(0, 10, 100), 1),
            (slice(-1, 10, 5), 3),
            (slice(-10, -1, 3), 3),
            (slice(15, 10, 1), 0),
            (slice(0, 10, -1), 0),
            (slice(0, 10, -3), 0),
            (slice(15, 10, -1), 5),
            (slice(10, 0, -1), 10),

            # none replacement (without len)
            (slice(None, 10, 1), 10),
            (slice(0, 10, None), 10),
        ]

        def test_len(s: slice, expected_len: int):
            iter_len = s.stop + 1  # simulate some iterable that is longer than the max_len

            enumerated_idxs = list(range(s.start or 0, s.stop, s.step or 1))
            enumerated_len = len(enumerated_idxs)

            result = slice_len(s, iter_len)
            self.assertEqual(result, expected_len, "Not same as expected!")
            self.assertEqual(result, enumerated_len, "Not same as enumerated!")

        def test_max_len(s: slice, expected_len: int):
            result = max_slice_len(s)
            self.assertEqual(result, expected_len,
                             "Max len was not equal! slice: {}. expected: {}. Actual: {}".format(s, expected_len,
                                                                                                 result))
        for case in simple_test_cases:
            s, expected = case
            with self.subTest("max_len {} -> {}".format(s, expected)):
                test_max_len(s, expected)
            with self.subTest("len vs enumerated {} -> {}".format(s, expected)):
                test_len(s, expected)