为什么 a[1:-1:-1] 和 a=[1,2,3] return []?

Why does a[1:-1:-1] with a=[1,2,3] return []?

我观察到如果 a 是一个包含元素 [1,2,3] 的列表(或 numpy 数组)并且我要求 a[1:-1:-1],那么我得到的是空列表。我希望得到 [2,1] 假设切片跨越可获得的索引,从 1 到 -1 递减,不包括最后一个值(不包括 -1),即索引 1 和 0。

实际行为可能有一定道理,但当需要从某个通用索引 i 到索引 i+m 获取数组 a 的子数组时,事情会比预期的更复杂](排除)以相反的顺序。人们倾向于写 a[i+m-1:i-1:-1] 但如果 i 设置为 0,这突然中断。它适用于所有 i 但零的事实看起来像一个令人讨厌的不一致。显然,有解决方法:

  1. 可以写 a[i+m-1-n:i-1-n:-1] 将所有内容偏移 -n,其中 n 是数组长度;或
  2. 可以写 a[i:i+m][::-1].

但是,在情况 1 中,需要知道数组长度显得相当不自然,而在情况 2 中,如果切片是在一个紧密的循环中完成的,那么双重索引似乎是一个不太合理的开销。

-1 作为索引具有特殊含义 [1],它被替换为最高可能 = 列表的最后一个索引。 所以 a[1:-1:-1] 变成 a[1:2:-1] 是空的。

[1] 实际上,Python 中的所有负指数都是这样工作的。 -1 表示列表的最后一个元素,-2 second-to-last,-3 表示之前的元素,依此类推。

List[1:-1:-1]表示List[start index : end index : jump]

列表中的索引:

Number 1 2 3
Index 0 1 2
Index -3 -2 -1

所以,如果我们获取列表 a[1,2,3] 并找到 a[1:-1:-1][= 的列表29=] 表示 starting index = 1, ending index = -1, jump = -1

所以,列表遍历

index 1 (i.e. number=2) to index -1 (i.e. number=3) but jump = -1 (means backward position)

So, return an empty list i.e. []

正如其他人指出的那样-1 因为终点具有特殊意义

In [66]: a=[1,2,3]

切回开头最好使用 None:

In [68]: a[1::-1]
Out[68]: [2, 1]
In [69]: a[1:None:-1]
Out[69]: [2, 1]

使用可能跨越边界的切片,任何一方都可能很棘手:

In [75]: [a[i+2-1:i-1:-1] for i in range(4)]
Out[75]: [[], [3, 2], [3], []]

简化一点:

In [77]: [a[i+2:i:-1] for i in range(-1,3)]
Out[77]: [[], [3, 2], [3], []]

我们可以使用 if 子句更正下边界:

In [78]: [a[i+2:None if i<0 else i:-1] for i in range(-1,3)]
Out[78]: [[2, 1], [3, 2], [3], []]

Numpy 从 Python 的序列索引中采用了这种行为,其中解释了规则 here(一些历史见下文)。具体脚注(5)为:

The slice of s from i to j with step k is defined as the sequence of items with index x = i + n*k such that 0 <= n < (j-i)/k. In other words, the indices are i, i+k, i+2*k, i+3*k and so on, stopping when j is reached (but never including j). When k is positive, i and j are reduced to len(s) if they are greater. When k is negative, i and j are reduced to len(s) - 1 if they are greater. If i or j are omitted or None, they become “end” values (which end depends on the sign of k). Note, k cannot be zero. If k is None, it is treated like 1.

因此,索引是根据0 <= n < (j-i)/k 的乘数n 生成的。对于您的具体示例 (j-i)/k < 0,因此不会计算任何索引。

对于 Numpy 数组 a[i:i+m][::-1] 生成底层数组的视图,即它的开销可以忽略不计,因此似乎是一个有效的解决方案。它清楚地传达了意图,即“取一个数组a的子数组,从一些通用索引i开始到索引i+m(排除)以相反的顺序”.

或者,如果 i 为零,您可以使用 None 作为停止参数:

a[i+m-1:(None if i==0 else i-1):-1]

历史

最初,Python 通过 __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 as list. 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 specific Slice type which could be used to also specify a so called stride (now step) in a form very similar to today's slice: 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 the slice type and would be passed to __getitem__ instead of __getslice__. So back then, a[i:j] was resolved as a.__getslice__(i, j) while a[i:j:k] was resolved as a.__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 to a[i::-1] for an array object a). 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 用户实现了切片语法)。 花了很长时间,直到 Python 2.3 为 built-in 类型实现了包含步骤的扩展切片功能(参见 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 2.4 版本)。