【问题标题】:Python list error: [::-1] step on [:-1] slicePython 列表错误:[::-1] 踩到 [:-1] 切片
【发布时间】:2017-05-16 19:04:02
【问题描述】:

我以为我了解了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 开始。
  • @ekhumoro:我做到了,而且有效。 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
  • @Josh。这与a[::-1] 有何不同?
  • @ekhumoro:好点,a[-2::-1](产生[8, 7, 6, 5, 4, 3, 2, 1, 0])实际上可能是答案,因为包含了起始索引。
  • @Josh,你说得对,a[-2::-1] 是我一直在寻找的解决方案,但直到下面讨论该步骤的 +/- 符号如何影响末端的行为。

标签: python slice reverse


【解决方案1】:

a[:-1:-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]

【讨论】:

  • 您如何解释a[::-1]、a[:0:-1] 和a[:-1:-1] 的结果之间的差异?或者换一种说法:空结束值代表什么实际数字?
  • 我觉得-len(a)-1可以用来代表结束(j,in 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 代替 (note 3),其计算结果为 -1。
【解决方案2】:

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

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

假设我们有[a: b: c]。索引的规则如下:

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

注意 1:Python 中的退化切片被优雅地处理:

  • 过大或过小的索引替换为len或0。
  • 小于下限的上限返回空列表或字符串或其他任何内容(对于正值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!

【讨论】:

    【解决方案3】:

    我通常发现切片 range-object 很有用(这仅在 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]
    

    【讨论】:

      【解决方案4】:

      Python 的切片起初看起来相当简单,但它们的行为实际上是quite complex(此处与注释 3 和 5 相关)。如果你有切片a[i:j:k]:

      • 如果i 或j 为负数,它们指的是a 末尾的索引(所以a[-1] 指的是a 的最后一个元素)
      • 如果未指定i 或j,或者是None,则它们默认以a 结尾,但哪个 结尾取决于k 的符号:

        • 如果k 是正数,你正在向前切片,所以i 变为0 并且j 变为len(a)
        • 如果k 为负数,则您正在向后切片,因此i 变为len(a) 并且j 变为a 开始之前的元素。

          注意:j不能替换为 -1,因为这样做会导致 Python 将 j 视为 last a 的元素而不是 a[0] 之前的(不存在的)元素。要获得所需的行为,您必须使用-len(a)-1(或-(len(a)+1))代替j,这意味着要到达a[j],切片从a的最后一个元素开始,左移@ 987654352@ 元素,然后再留下一个元素,在a 开始之前结束,因此在切片中包括a[0]。

      因此,a[:-1:-1] 表示“从a 的末尾,即a[-1](因为i 未指定且k 为负数),到a 的最后一个元素(因为@ 987654361@),步长为 -1"。 i 和 j 是相等的——你在同一个地方开始和停止切片——所以表达式的计算结果是一个空列表。

      要反转a[:-1],您可以使用a[-2::-1]。这样,切片从倒数第二个元素a[-2](因为a[:-1] 不包括a[-1])开始并向后直到元素“之前”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]
      

      【讨论】:

      • "a[:-1:-1] 表示您从头开始(索引 0)并向后退,直到碰到最后一个元素(索引 -1)。不对。请参阅docs for sequence operations 下的注释 #5:在切片 a[i:j:k] 中,“如果 i 或 j 被省略或无,它们将成为“结束”值(结束取决于 k 的符号)。这就是为什么a[:1:-1] 给出最后一个len(a) - 2 元素,而不是第一个元素;相当于a[len(a)-1:1:-1]。
      • @ThisSuitIsBlackNot 我认为这是我没有掌握的关键:“哪一端取决于 k 的符号”。谢谢!
      • @ThisSuitIsBlackNot,马特:你完全正确,很抱歉我错了。我将删除不正确的信息并重写。
      【解决方案5】:

      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
      

      【讨论】:

        【解决方案6】:

        用简单的方式理解如果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] 为您提供了完整的列表并且很有意义。

        【讨论】:

        • 请澄清您的答案。说没有任何意义或有意义不是解释。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-29
        • 2020-02-21
        • 1970-01-01
        • 2018-01-30
        • 2017-10-23
        • 2017-06-24
        相关资源
        最近更新 更多