在Python中从切片对象检索切片长度
标题解释了如何从对象中提取2在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 但对于任意大的切片,这将失败 谢谢,我在其他帖子中找不到答案 对此没有完整的答案切片不会给出
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)