Python list error: [::-1] step on [:-1] slice
Python list error: [::-1] step on [:-1] slice
我以为我了解了python中列表切片的基础知识,但是在切片上使用负步时收到意外错误,如下:
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-1:-1]
[]
(请注意,这是 Python 3.5 中的 运行)
为什么 a[:-1:-1] 不以与 a[::-1] 遍历整个列表相同的方式反向遍历 a[:-1] 切片?
我知道您也可以使用 list.reverse(),但我想更好地理解底层的 python 切片功能。
a[:-1:-1]
中的第一个-1
并不代表你认为的那样。
在切片中,负 start/end 索引不按字面解释。相反,它们用于方便地引用列表的末尾(即它们相对于 len(a)
)。无论切片的方向如何,都会发生这种情况。
这意味着
a[:-1:-1]
相当于
a[:len(a)-1:-1]
在反向切片时省略时,起始索引默认为len(a)-1
,使得上面等同于
a[len(a)-1:len(a)-1:-1]
这总是给出一个空列表,因为开始和结束索引是相同的并且结束索引是互斥的。
要反向切片直到并包括第零个元素,您可以使用以下任何符号:
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:None:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-len(a)-1:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Python 的切片初看起来相当简单,但它们的行为实际上是 quite complex(注释 3 和 5 与此处相关)。如果你有一个切片 a[i:j:k]
:
- 如果
i
或j
是负数,它们指的是从a
末尾开始的索引(所以a[-1]
指的是[=14的最后一个元素=])
如果没有指定i
或j
,或者是None
,它们默认到a
的结尾,但是哪个结束取决于k
的符号:
- 如果
k
为正数,则向前切分,因此 i
变为 0,j
变为 len(a)
如果 k
为负数,则向后切片,因此 i
变为 len(a)
并且 j
变为开始之前的元素a
.
注意: j
不能将替换为-1,因为这样做会导致Python将 j
视为 a
的 last 元素,而不是 a[0]
之前的(不存在的)元素。要获得所需的行为,您必须使用 -len(a)-1
(或 -(len(a)+1)
)代替 j
,这意味着要到达 a[j]
,切片从a
,向左移动 len(a)
个元素,然后再向左移动一个元素,在 a
开始之前结束,因此在切片中包含 a[0]
。
因此,a[:-1:-1]
表示"go from the end of a
, which is a[-1]
(since i
is unspecified and k
is negative), to the last element of a
(since j == -1
), with step size of -1"。 i
和 j
是相等的——你在同一个地方开始和停止切片——所以表达式的计算结果是一个空列表。
要反转a[:-1]
,可以使用a[-2::-1]
。这样,切片从倒数第二个元素 a[-2]
开始(因为 a[:-1]
不包括 a[-1]
)并向后移动直到元素 "before" a[0]
,这意味着a[0]
包含在切片中。
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[-2::-1]
[8, 7, 6, 5, 4, 3, 2, 1, 0]
当您键入 [1, 2, 3, ...][1:4:1]
时,它与 [1, 2, 3, ...][slice(1, 4, 1)]
相同。所以 1:4:1
是 slice
对象的 shorthand。 slice
签名是 slice(stop)
或 slice(start, stop[, step])
,您也可以使用 None
作为参数。
:: -> slice(None, None, None)
:4 -> slice(4)
# and so on
假设我们有[a: b: c]
。 索引的规则如下:
- 首先检查
c
。默认为+1
,c
的符号表示步进的前进或后退方向。 c
的绝对值表示步长。
- 比
a
勾选。当 c
为正数或 None
时,a
的默认值为 0
。当 c
为负数时,a
的默认值为 -1
.
- 终于
b
被选中了。当 c
为正数或 None
时,b
的默认值为 len
。当 c
为负数时 b
的默认值为 -(len+1)
.
注释 1:Python 中的退化切片被优雅地处理:
- 太大或太小的索引被替换为
len
或0
。
- 小于下限的上限 returns 空列表或字符串或其他任何内容(对于正数
c
)。
注2:粗略地说,Python在条件(a < b) if (c > 0) else (a > b)
为True
时拾取元素(更新a += c
每一步)。此外,所有负索引都替换为 len - index
.
如果将这些规则和注释结合起来,就会明白为什么得到一个空列表。在你的情况下:
In[1]: [1, 2, 3, 4, 5, 6][:-1:-1] # `c` is negative so `a` is -1 and `b` is -1
Out[1]: []
# it is the same as:
In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1] # which will produce you an empty list
Out[2]: []
关于切片符号的讨论非常好:Explain Python's slice notation!
slice
与 range
的工作方式类似,当您将 step
参数设置为负数时,start
和 stop
参数在相反的方向。
>>> list(range(9, -1, -1)) == a[::-1]
True
可能有助于更清楚地说明这一点的一些示例:
>>> a[6:2:-2]
[6, 4]
>>> a[0:None:1] == a[::]
True
>>> a[-1:None:-1] == a[::-1]
True
>>> a[-2:None:-1] == a[:-1][::-1]
True
我通常发现对 range
对象进行切片很有用(这仅在 python3 中可行 - 在 python2 range
中生成 list
并且 xrange
不能被切片)如果我需要查看哪些索引用于给定长度的列表:
>>> range(10)[::-1]
range(9, -1, -1)
>>> range(10)[:-1]
range(0, 9)
最后一个案例:
>>> range(10)[:-1:-1]
range(9, 9, -1)
这也解释了发生了什么。第一个索引是 9,但 9 不低于停止索引 9(请注意,在 python 中,停止索引是 excluded),因此它在不提供任何元素的情况下停止。
请注意,索引也可以按顺序应用:
>>> list(range(10))[::-1][:-1] # first reverse then exclude last item.
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(10))[:-1][::-1] # other way around
[8, 7, 6, 5, 4, 3, 2, 1, 0]
通俗的理解就是如果最后的a[::-1]
-1把字符串反转。
现在
a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
a[::-1]=[9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
now a[:-1:-1]
中间的 -1 没有任何意义,因为现在它是第一个元素,这将给出一个空列表。
而 a[-1::-1]
为您提供了完整的列表并且很有意义。
我以为我了解了python中列表切片的基础知识,但是在切片上使用负步时收到意外错误,如下:
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-1:-1]
[]
(请注意,这是 Python 3.5 中的 运行)
为什么 a[:-1:-1] 不以与 a[::-1] 遍历整个列表相同的方式反向遍历 a[:-1] 切片?
我知道您也可以使用 list.reverse(),但我想更好地理解底层的 python 切片功能。
a[:-1:-1]
中的第一个-1
并不代表你认为的那样。
在切片中,负 start/end 索引不按字面解释。相反,它们用于方便地引用列表的末尾(即它们相对于 len(a)
)。无论切片的方向如何,都会发生这种情况。
这意味着
a[:-1:-1]
相当于
a[:len(a)-1:-1]
在反向切片时省略时,起始索引默认为len(a)-1
,使得上面等同于
a[len(a)-1:len(a)-1:-1]
这总是给出一个空列表,因为开始和结束索引是相同的并且结束索引是互斥的。
要反向切片直到并包括第零个元素,您可以使用以下任何符号:
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:None:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-len(a)-1:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Python 的切片初看起来相当简单,但它们的行为实际上是 quite complex(注释 3 和 5 与此处相关)。如果你有一个切片 a[i:j:k]
:
- 如果
i
或j
是负数,它们指的是从a
末尾开始的索引(所以a[-1]
指的是[=14的最后一个元素=]) 如果没有指定
i
或j
,或者是None
,它们默认到a
的结尾,但是哪个结束取决于k
的符号:- 如果
k
为正数,则向前切分,因此i
变为 0,j
变为len(a)
如果
k
为负数,则向后切片,因此i
变为len(a)
并且j
变为开始之前的元素a
.注意:
j
不能将替换为-1,因为这样做会导致Python将j
视为a
的 last 元素,而不是a[0]
之前的(不存在的)元素。要获得所需的行为,您必须使用-len(a)-1
(或-(len(a)+1)
)代替j
,这意味着要到达a[j]
,切片从a
,向左移动len(a)
个元素,然后再向左移动一个元素,在a
开始之前结束,因此在切片中包含a[0]
。
- 如果
因此,a[:-1:-1]
表示"go from the end of a
, which is a[-1]
(since i
is unspecified and k
is negative), to the last element of a
(since j == -1
), with step size of -1"。 i
和 j
是相等的——你在同一个地方开始和停止切片——所以表达式的计算结果是一个空列表。
要反转a[:-1]
,可以使用a[-2::-1]
。这样,切片从倒数第二个元素 a[-2]
开始(因为 a[:-1]
不包括 a[-1]
)并向后移动直到元素 "before" a[0]
,这意味着a[0]
包含在切片中。
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[-2::-1]
[8, 7, 6, 5, 4, 3, 2, 1, 0]
当您键入 [1, 2, 3, ...][1:4:1]
时,它与 [1, 2, 3, ...][slice(1, 4, 1)]
相同。所以 1:4:1
是 slice
对象的 shorthand。 slice
签名是 slice(stop)
或 slice(start, stop[, step])
,您也可以使用 None
作为参数。
:: -> slice(None, None, None)
:4 -> slice(4)
# and so on
假设我们有[a: b: c]
。 索引的规则如下:
- 首先检查
c
。默认为+1
,c
的符号表示步进的前进或后退方向。c
的绝对值表示步长。 - 比
a
勾选。当c
为正数或None
时,a
的默认值为0
。当c
为负数时,a
的默认值为-1
. - 终于
b
被选中了。当c
为正数或None
时,b
的默认值为len
。当c
为负数时b
的默认值为-(len+1)
.
注释 1:Python 中的退化切片被优雅地处理:
- 太大或太小的索引被替换为
len
或0
。 - 小于下限的上限 returns 空列表或字符串或其他任何内容(对于正数
c
)。
注2:粗略地说,Python在条件(a < b) if (c > 0) else (a > b)
为True
时拾取元素(更新a += c
每一步)。此外,所有负索引都替换为 len - index
.
如果将这些规则和注释结合起来,就会明白为什么得到一个空列表。在你的情况下:
In[1]: [1, 2, 3, 4, 5, 6][:-1:-1] # `c` is negative so `a` is -1 and `b` is -1
Out[1]: []
# it is the same as:
In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1] # which will produce you an empty list
Out[2]: []
关于切片符号的讨论非常好:Explain Python's slice notation!
slice
与 range
的工作方式类似,当您将 step
参数设置为负数时,start
和 stop
参数在相反的方向。
>>> list(range(9, -1, -1)) == a[::-1]
True
可能有助于更清楚地说明这一点的一些示例:
>>> a[6:2:-2]
[6, 4]
>>> a[0:None:1] == a[::]
True
>>> a[-1:None:-1] == a[::-1]
True
>>> a[-2:None:-1] == a[:-1][::-1]
True
我通常发现对 range
对象进行切片很有用(这仅在 python3 中可行 - 在 python2 range
中生成 list
并且 xrange
不能被切片)如果我需要查看哪些索引用于给定长度的列表:
>>> range(10)[::-1]
range(9, -1, -1)
>>> range(10)[:-1]
range(0, 9)
最后一个案例:
>>> range(10)[:-1:-1]
range(9, 9, -1)
这也解释了发生了什么。第一个索引是 9,但 9 不低于停止索引 9(请注意,在 python 中,停止索引是 excluded),因此它在不提供任何元素的情况下停止。
请注意,索引也可以按顺序应用:
>>> list(range(10))[::-1][:-1] # first reverse then exclude last item.
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(10))[:-1][::-1] # other way around
[8, 7, 6, 5, 4, 3, 2, 1, 0]
通俗的理解就是如果最后的a[::-1]
-1把字符串反转。
现在
a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
a[::-1]=[9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
now a[:-1:-1]
中间的 -1 没有任何意义,因为现在它是第一个元素,这将给出一个空列表。
而 a[-1::-1]
为您提供了完整的列表并且很有意义。