我观察到,如果 a
是带有Elements [1,2,3]
的列表(或Numpy数组),我要求 a [1: - - - 1:-1]
,然后我得到空列表。我希望获得 [2,1]
,假设切片跨越可从1到-1的索引,排除了最后一个值(不包括-1),那就是索引1和0。
实际行为可能具有一定的理由,但是当一个人需要从某些通用索引 i 开始到索引时,就可以使事情比预期的比预期更复杂。 i+m
(排除)以相反的顺序。一个人会倾向于编写 a [i+m-1:i-1:-1]
,但如果 i
设置为0。对于所有 i
,但零看起来像是一种令人讨厌的矛盾。显然,有解决方法:
- 一个人可以编写
a [i+m-1-n:i-1-n:-1]
通过 -n
在其中 code
> n 是数组长度;或者
- 可以写
a [i:i+m] [:: - 1]
。
但是,在情况1中,需要知道阵列长度显得不自然,如果在紧密的环中进行切片,则双重索引似乎是不是很合理的开销。
-
有什么重要的原因是我不遗余力地行为是如此重要吗?
-
Numpy社区是否考虑过此问题?
-
有比我想出的更好的解决方法了吗?
I am observing that if a
is a list (or a numpy array) with elements [1,2,3]
and I ask for a[1:-1:-1]
, then I get the empty list. I would expect to get [2,1]
assuming that the slicing spans the indexes obtainable decrementing from 1 to -1 excluding the last value (that is excluding -1), that is indexes 1 and 0.
The actual behavior may have some justification but makes things more complex than expected when one needs to take a subarray of an array a
starting from some generic index i
to index i+m
(excluded) in reverse order. One would tend to write a[i+m-1:i-1:-1]
but this suddenly breaks if i
is set to 0. The fact that it works for all i
but zero looks like a nasty inconsistency. Obviously, there are workarounds:
- one could write
a[i+m-1-n:i-1-n:-1]
offsetting everything by -n
where n
is the array length; or
- one could write
a[i:i+m][::-1]
.
However, in case 1 the need to know the array length appears rather unnatural and in case 2 the double indexing appears as a not very justified overhead if the slicing is done in a tight loop.
-
Is there any important reason that I am missing for which it is important that the behavior is as it is?
-
Has this issue been considered by the NumPy community?
-
Is there some better workaround than those I came up with?
发布评论
评论(4)
Numpy从Python的序列索引中采用了这种行为,该规则被解释了这里(有关一些历史记录,请参见下文)。特别是脚注(5)读:
因此,这些索引是从乘数生成的
n
,但要受0< = n< (JI)/K
。对于您的特定示例(JI)/K< 0
,因此没有计算索引。对于numpy数组
a [i:i+m] [:: - 1]
生成了基础数组的视图,即它的开销可以忽略不计,因此似乎是一个有效的解决方案。显然,它传达了意图,即“从某些通用索引 i 开始,以a
的子阵列为子阵列 indexi+m
(排除)以相反的顺序“ 。另外,您可以使用
无
作为停止参数,如果i
为零:历史
最初,Python通过
__ getslice __
/release/1.4/ref/ref3.html#hdr5“ rel =“ nofollow noreferrer”>在这里)不允许步骤参数,即它仅使用了2个argument form:a [[
a [ I:J]
。这是通过内置序列(例如list
)实现的。那时,在1995年左右,在矩阵sig (特殊兴趣组)。该前任实现了一个特定的slice
类型,该类型也可以用来指定一个所谓的stride
(现在step
),形式与今天非常相似slice
:例如a [slice(none,none,none,2)]
。被要求扩展python的语法以允许今天知道的3型切片:a [:: 2]
(请参阅eg 此线程)。这是以键入并将传递给__ getItem __
,而不是__ getlice __
。因此,当时,a [i:j]
被解析为a .__ getlice __(i,j)
而a [i:j:k]
被解析为a .__ getItem __(slice(i,j,k))
。那时,数值python甚至允许使用2型切片,将第二个参数解释为大步(请参阅 docs ; ega [i:-1]
等效于a [i :: - 1] 对于数组对象
a
)。阵列的索引是针对Python序列的索引如何工作的:包括开始索引,不包括停止索引(请参见在这里)。这也适用于负面大步(步骤),因此提供了今天可以观察到的行为。该决定可能是基于最小令人惊讶的原理//docs.python.org/release/1.5/lib/node10.html#section00315000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000来源程序。花了很长时间,直到Python 2.3为内置类型实施了扩展的切片功能,包括一项步骤(请参阅什么新功能和注意,该文档的2.3版本包含一个错误的切片描述,该阶跃是固定用于2.4版本)。
Numpy has adopted this behavior from Python's sequence indexing for which the rules are explained here (for some history see below). Specifically footnote (5) reads:
So the indices are generated from multipliers
n
subject to0 <= n < (j-i)/k
. For your specific example(j-i)/k < 0
and hence no indices are computed.For Numpy arrays
a[i:i+m][::-1]
generates a view of the underlying array, i.e. it has negligible overhead and thus appears to be a valid solution. It clearly conveys the intent, namely "take a subarray of an arraya
starting from some generic indexi
to indexi+m
(excluded) in reverse order".Alternatively, you can use
None
as the stop argument ifi
is zero:History
Originally, Python implemented slicing syntax via
__getslice__
(see also here) which didn't allow a step argument, i.e. it only used the 2-argument form:a[i:j]
. This was implemented by built-in sequences such aslist
. Back then, around 1995, the predecessor of Numpy, Numerical Python, was developed and discussed within the MATRIX-SIG (special interest group). This predecessor implemented a specificSlice
type which could be used to also specify a so calledstride
(nowstep
) in a form very similar to today'sslice
: e.g.a[Slice(None, None, 2)]
. It was asked to extend Python's syntax to allow for the 3-form slicing known today:a[::2]
(see e.g. this thread). This got implemented in form of theslice
type and would be passed to__getitem__
instead of__getslice__
. So back then,a[i:j]
was resolved asa.__getslice__(i, j)
whilea[i:j:k]
was resolved asa.__getitem__(slice(i, j, k))
. Back then, Numerical Python even allowed "reverse" slicing with the 2-form, interpreting the second argument as the stride (see the docs; e.g.a[i:-1]
was equivalent toa[i::-1]
for an array objecta
). Indexing of arrays was oriented at how indexing for Python sequences worked: including the start index, excluding the stop index (see here). This applied to negative stride (step) as well, hence providing the behavior that can be observed today. The decision was probably based on the principle of least surprise (for "standard" Python users).It took a long time until Python 2.3 where the extended slicing feature including a step was implemented for the built-in types (see what's new and the docs; note that the 2.3 version of the docs contained a wrong description of slicing with step which was fixed for the 2.4 release).
-1作为索引具有特殊的含义[1],它被列表的最高=最后一个索引代替。
因此,
a [1:-1:-1]
变为空的代码> A [1:2:-1] 是空的。[1]实际上,Python中的所有负数索引都这样工作。 -1是指列表的最后一个元素,-2二次到二次,-3之前的元素,依此类推。
-1 as an index has a special meaning [1], it's replaced with the highest possible = last index of a list.
So
a[1:-1:-1]
becomesa[1:2:-1]
which is empty.[1] Actually, all negative indices in Python work like that. -1 means the last element of a list, -2 the second-to-last, -3 the one before that and so on.
列表[1:-1:-1] 表示列表[开始索引:end Index:jump]
列表中的索引:
因此,如果我们列出列表 a [1,2,3] 并找到 a [1:: -1:-1] 表示<代码>启动index = 1,end index = -1,jump = -1
so,列表穿越
List[1:-1:-1] means List[start index : end index : jump]
Indexing in List:
So, if we take list a[1,2,3] and find list of a[1:-1:-1] means
starting index = 1, ending index = -1, jump = -1
So, list traversing through the
正如其他人所指出的
-1
,终点具有特殊的意思,将其缩回开始,最好使用
none
:处理可以跨越边界的切片,任一方都可能很棘手:
简化一点:
如果子句:
As others noted
-1
as end point has special meaningSlice back to the beginning is best done with
None
:Working with slices that could cross boundaries, either side can be tricky:
simplify a bit:
We can correct the lower boundary by using a
if
clause: