标题解释了自己,如何从对象中获得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)
一种可能的解决方法是使用切片对象
切片列表a = [1,2,3,4,5]
len(a[slice(0,2)]) # 2
但是对于任意大的切片,这都会失败。
谢谢,我在其他帖子中找不到答案。
答案 0 :(得分:4)
对此没有完整的答案。 slice
没有给出长度,因为结果的长度总是取决于被切片序列的大小,短序列(包括空序列)将产生更少的项目,如果{ {1}}是无界的,然后长度将与序列的长度一起增长;一个slice
可能只是顺序结束"拥有slice
或start
stop
。
为了快速简便地计算已知长度序列的长度,您只需将None
与Py3' .indices
(或range
组合在Py2中,虽然xrange
对Py3 xrange
没有的值有限制。 slice.indices
gives you the concrete start
, stop
and stride
values derived when a slice
applies to a sequence of a given length,它基本上是您在C样式range
循环中填写的值,该循环遍历与for
相同的索引:
slice
因此,要计算应用于包含1000个元素的序列时 for (ssize_t i = start; i < stop; i += stride)
的长度,您可以这样做:
slice
如果您使用的是Python 2,并且您的值可能超过>>> len(range(*slice(0, 2).indices(1000)))
2
>>> len(range(*slice(10, None, 3).indices(1000)))
330
可以处理的值(它仅限于界限和总长度等于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
超出len
可以容纳的序列,所以这并不是#&# 39;在Py2中使用long
获得任何结果。留给那些感兴趣的人,但除非你perform the work slice
does to convert negative values and None
to concrete values based on the sequence length.感叹,否则解决方法不会解决任何问题。
答案 1 :(得分:3)
所以看起来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中为您提供正确答案的原因。(范围对象是一个生成器,幸运的是定义了__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
答案 2 :(得分:1)
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_len
的情况下获得切片的“最大长度”,
然后我们可以通过将 src_len
(列表的长度或您想要迭代的任何内容)作为切片的 stop
(如果它小于当前的 stop
)来构建它。
但这仍然存在找到“最大长度”的问题。
python 中的切片构造创建了一种 Arithmetic Set。
其中 a0
== start
、d
== step
、n
== len
一个公式告诉我们:a_n = a0+ (n-1)d
[a_n
是序列的第 n 个元素]
如果我们将 stop
视为 a_n
,则:stop = start + (len - 1) * step
。
重新排列我们得到:len = [(stop-start)/step] + 1
.
这很好地为我们处理了反向迭代(即 [10:0:-1]),
但它通常会返回一个浮点数,因为停止可能不是开始之后的全部“步骤”。 (即对于 [0:10:3],(10-0) / 3 给我们 3.3333...)。
使用 ceil
解决了这个问题。
剩下的唯一问题是负面结果([10:0:1] 会给我们 (0-10)/1 = -10),但实际的“长度”应该为零。
解决方案是通过返回 dsteps if dsteps >= 0 else 0
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)
答案 3 :(得分:0)
长度取决于要切片的目标对象。 但是可以定义一个最大长度。
示例
定义这样的最大长度函数
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
由于切片未定义start
属性,因此最后一次呼叫失败。
答案 4 :(得分:-3)
>>> slice(0,2).__getattribute__('stop')
2
>>> slice(0,2).__getattribute__('start')
0