2017-01-02 64 views
15

我以为我理解了python中列表切片的基础知识,但在切片上使用负步骤时收到意外错误,因为如下:Python列表错误:[:: -1]踩在[:-1]切片上

>>> 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中运行)

为什么没有一个: - :通过一个[1 -1]的反向步骤: - 在[1]片与通过[:: - 1]整个列表一样的方式?

我知道你也可以使用list.reverse(),但是试图更好地理解底层python slice功能。

+1

我认为你正在寻找''一个[-1 :: - 1]''。第一个索引给出了开始,第二个索引给出了结束,并且你想从索引-1开始。 – jakevdp

+0

@ekhumoro:我做了,它的工作原理。 '[9,8,7,6,5,4,3,2,1,0]' – Josh

+1

@Josh。这与'a [:: - 1]'有什么不同? – ekhumoro

回答

19

第一个-1a[:-1:-1]并不意味着你认为它的确如此。

在切片中,负面的开始/结束索引不能字面解释。相反,它们用于方便地引用列表的末尾(即它们相对于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] 
+0

你会如何解释'a [:: - 1]','a [:0:-1]'和'a [: - 1:-1]'的结果之间的区别?或者换句话说:实际的数字是由空的最终值表示的? – ekhumoro

+0

我认为'-len(a)-1'可以用来表示结尾('j',在'a [i:j:k]')。如果'a'是'range(10)',那么'a [: - len(a)-1:-1]'产生'[9,8,7,6,5,4,3,2,1, 0]'。在这里,'-len(a)-1'评估为-11;这是否定的,所以使用'len(a)+ -11'([注3](https://docs.python.org/3/library/stdtypes.html#common-sequence-operations)),评估为-1。 – Josh

0

Python的片乍看相当简单的,但他们的行为实际上是quite complex(注3和5在这里相关)。如果你有一个切片a[i:j:k]

  • 如果ij是否定的,他们指的是指数的a末(所以a[-1]a最后一个元素)
  • 如果ij是未指定,或者是None,它们默认的a两端,但其两端依赖于k符号:

    • 如果k是肯定的,你向前切片,所以i变为0,j成为len(a)
    • 如果k是否定的,你切片反了,所以i变得len(a)j开始前将成为元素a

      NB:j无法用-1来代替,因为这样做会导致Python来对待ja最后元素,而不是a[0]前(不存在)的元素。为了获得所需的行为,您必须使用-len(a)-1(或-(len(a)+1))代替j,这意味着要获得a[j],切片从a的最后一个元素开始,向左离开len(a)个元素,然后剩下一个元素,最后结束之前a开始,因此包括a[0]在切片中。

因此,a[:-1:-1]意味着“从a结束,这是a[-1]去(因为i是不确定的,并且k是负的),以的a(因为j == -1)的最后一个元素,以步长大小的-1“。 ij是相等的 - 你在同一个地方开始和停止切片 - 因此表达式评估为空列表。

要反转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] 
+2

“'a [: - 1:-1]'意味着你从开始(索引0)开始并向后走,直到你碰到最后一个元素(索引-1)。”不对。请参阅[文档序列操作](https://docs.python.org/3/library/stdtypes.html#common-sequence-operations)下的注释#5:在片段'a [i:j:k] ',“如果i或j被省略或None,它们变成”结束“值(这取决于k的符号)。”这就是为什么'a [:1:-1]给出最后一个'len(a) - 2'元素,而不是第一个元素;它相当于'a [len(a)-1:1:-1]'。 – ThisSuitIsBlackNot

+1

@ThisSuitIsBlack我不认为这是我没有把握的关键:“哪一端取决于k的符号”。谢谢! –

+0

@ThisSuitIsBlackNot,Matt:你说得很对,我很抱歉我弄错了。我将删除不正确的信息并重写。 – Josh

6

当你键入[1, 2, 3, ...][1:4:1]它是一样的[1, 2, 3, ...][slice(1, 4, 1)]。所以1:4:1slice对象的简写。 slice签名是slice(stop)slice(start, stop[, step]),您也可以使用None作为参数。

:: -> slice(None, None, None) 
:4 -> slice(4) 
# and so on 

假设我们有[a: b: c]。对于指数规则将作如下安排:

  1. 首先c检查。默认为+1c的符号表示该步骤的前进或后退方向。 c的绝对值表示步长。
  2. a被选中。当c为正或None时,a的默认值为0。当c为负值时,a的默认值为-1
  3. 最后检查b。当c为正或None时,b的默认值为len。当c为负时,b的默认值为-(len+1)

注1:在Python切片简并在正常处理:

  • 过大或过小被替换len0索引。
  • 小于下限的上限返回空列表或字符串或其他任何内容(对于积极的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

1

时,你的step参数负数slice的工作原理类似于在range,该startstop论点相反的方向运行。

>>> 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 
4

我通常会发现它有用分割一个range -object(这是唯一可能在python3 - 在python2 range产生listxrange不能切片),如果我需要看哪些指标被用于给定长度的列表:

>>> 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中,停止索引是,不包括),所以它停止时没有给出任何元素。

注意索引,也可以按顺序应用:

>>> 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]