Python列表错误:[: - 1]踩到[:-1]切片

时间:2017-01-02 17:15:31

标签: python slice reverse

我认为我理解了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中运行)

为什么没有[: - 1:-1]反向跳过a [: - 1]切片的方式与通过[:: - 1]整个列表的方式相同?

我意识到你也可以使用list.reverse(),但试图更好地理解底层的python切片功能。

6 个答案:

答案 0 :(得分:22)

-1中的第一个a[:-1:-1]并不代表您的想法。

在切片中,负起始/结束索引不按字面解释。相反,它们用于方便地引用列表的末尾(即它们相对于len(a))。无论切片的方向如何,都会发生这种情况。

这意味着

a[:-1:-1]

相当于

a[:len(a)-1:-1]

在反向切片期间省略时,起始索引默认为len(a)-1,使上述内容等同于

a[len(a)-1:len(a)-1:-1]

这总是给出一个空列表,因为开始和结束索引是相同的,而结束索引是独占的。

要反向切换到第0个元素并包含第0个元素,您可以使用以下任何符号:

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

答案 1 :(得分:8)

当您输入[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。当cb的否定默认值为-(len+1)时。
  4. 注1 :优雅地处理Python中的退化切片:

    • 过大或过小的索引会被len0替换。
    • 小于下限的上限返回空列表或字符串或其他任何内容(对于正c)。

    注意2 :粗略地说,当条件(a < b) if (c > 0) else (a > b)True时,Python会选择元素(每一步都更新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

答案 2 :(得分:4)

我通常发现切片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中,停止索引是排除)所以它在没有给出任何元素的情况下停止。

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

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

答案 3 :(得分:1)

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成为{{{}之前的元素1}}。

      注意: a 无法替换为-1,因为这样做会导致Python将j视为最后的 <{1}}的元素,而不是j之前的(不存在)元素。要获得所需的行为,您必须使用a(或a[0])代替-len(a)-1,这意味着要转到-(len(a)+1),切片将从最后一个元素开始j向左移动a[j]个元素,然后再添加一个元素,在a开始之前结束,因此在切片中包含len(a)

因此,a表示&#34;从a[0]结束,a[:-1:-1](因为a未指定且a[-1]为否定),到i的最后一个元素(从k开始),步长为-1和#34;。 aj == -1相等 - 您在同一个地方开始和停止切片 - 因此表达式的计算结果为空列表。

要反转i,您可以使用j。这样,切片从倒数第二个元素a[:-1]开始(因为a[-2::-1]不包括a[-2])并且向后移动直到元素&#34;之前&#34; a[:-1],表示切片中包含a[-1]

a[0]

答案 4 :(得分:1)

slice的工作方式类似于range,因为当您将step参数设为负数时,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

答案 5 :(得分: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];

现在a[:-1:-1] -1中间没有任何意义,因为现在它是第一个元素,这将提供一个空列表。 而a[-1::-1]为您提供了完整的清单,很有意义。