Python列表错误:[:::-1]step on [:-1] slice

时间:2022-09-04 11:24:59

I thought I understood the basics of list slicing in python, but have been receiving an unexpected error while using a negative step on a slice, as follows:

我认为我理解python中的list切片的基本原理,但在使用一个切片的负步骤时,却收到了一个意外的错误,如下:

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

(Note that this is being run in Python 3.5)

(注意,这是在Python 3.5中运行的)

Why doesn't a[:-1:-1] reverse step through the a[:-1] slice in the same manner as it does through the whole list with a[::-1]?

为什么a[::-1:-1]不像a[:-1]那样,用a[::-1]逆步骤遍历整个列表?

I realize that you can use list.reverse() as well, but trying to understand the underlying python slice functionality better.

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

5 个解决方案

#1


21  

The first -1 in a[:-1:-1] doesn't mean what you think it does.

a[:-1:-1]中的第一个-1并不是你认为的那样。

In slicing, negative start/end indices are not interpreted literally. Instead, they are used to conveniently refer to the end of the list (i.e. they are relative to len(a)). This happens irrespectively of the direction of the slicing.

在切片中,负的起始/结束索引不是逐字解释的。相反,它们被用来方便地引用列表的末尾(即它们相对于len(a))。这与切片的方向无关。

This means that

这意味着

a[:-1:-1]

is equivalent to

相当于

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

When omitted during reverse slicing, the start index defaults to len(a)-1, making the above equivalent to

当在反向切片过程中被忽略时,开始索引默认为len(a)-1,使上面的操作等价于

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

This always gives an empty list, since the start and end indices are the same and the end index is exclusive.

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

To slice in reverse up to, and including, the zeroth element you can use any of the following notations:

要对零元素进行反向切片,您可以使用以下任何一种表示法:

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

#2


6  

When you type [1, 2, 3, ...][1:4:1] it is the same as [1, 2, 3, ...][slice(1, 4, 1)]. So 1:4:1 is the shorthand for slice object. slice signature is slice(stop) or slice(start, stop[, step]) and you can also use None for arguments.

当你键入[1,2,3,…[1:4:1]和[1,2,3,…][片(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

Suppose we have got [a: b: c]. Rules for indices will be as follows:

假设我们有[a: b: c]。指数规则如下:

  1. First c is checked. Default is +1, sign of c indicates forward or backward direction of the step. Absolute value of c indicates the step size.
  2. 首先检查c。默认为+1,c的符号表示步骤的向前或向后方向。c的绝对值表示步长。
  3. Than a is checked. When c is positive or None, default for a is 0. When c is negative, default for a is -1.
  4. 检查。当c为正或无时,a的默认值为0。当c为负时,a的默认值是-1。
  5. Finally b is checked. When c is positive or None, default for b is len. When c is negative default for b is -(len+1).
  6. 最后检查。当c为正或无时,b的默认值为len。当c为负时,b为-(len+1)

Note 1: Degenerated slices in Python are handled gracefully:

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

  • the index that is too large or too small is replaced with len or 0.
  • 索引太大或太小将被len或0替换。
  • an upper bound smaller than the lower bound returns an empty list or string or whatever else (for positive c).
  • 一个小于下界的上界返回一个空的列表或字符串或其他任何东西(对于正c)。

Note 2: Roughly speaking, Python picks up elements while this condition (a < b) if (c > 0) else (a > b) is True (updating a += c on every step). Also, all negative indices are replaced with len - index.

注2:粗略地说,Python在这个条件(a < b)的情况下(c > 0)是True(在每个步骤中更新a += c)。而且,所有的负指标都被len - index所取代。

If you combine this rules and notes it will make sense why you got an empty list. In your case:

如果你把这些规则和注释结合起来,你就会明白为什么你会得到一个空列表。在你的例子:

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

There is very good discussion about slice notation: Explain Python's slice notation!

关于切片表示法有很好的讨论:解释Python的切片表示法!

#3


4  

I generally find it useful to slice a range-object (this is only possible in python3 - in python2 range produces a list and xrange can't be sliced) if I need to see which indices are used for a list of a given length:

如果我需要查看给定长度的列表使用的是哪个索引,我通常会发现切片一个范围对象是有用的(这在python3中是唯一可能的——在python2中,范围产生一个列表,而xrange不能被分割):

>>> range(10)[::-1]  
range(9, -1, -1)

>>> range(10)[:-1]  
range(0, 9)

And in your last case:

最后一个例子是

>>> range(10)[:-1:-1]
range(9, 9, -1)

This also explains what happened. The first index is 9, but 9 isn't lower than the stop index 9 (note that in python the stop index is excluded) so it stops without giving any element.

这也解释了发生了什么。第一个索引是9,但是9不低于停止索引9(注意在python中停止索引被排除),所以它停止时不提供任何元素。

Note that indexing can also be applied sequentially:

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

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


1  

Python's slices seem fairly simple at first, but their behaviour is actually quite complex (notes 3 and 5 are relevant here). If you have a slice a[i:j:k]:

首先,Python的切片看起来相当简单,但是它们的行为实际上相当复杂(notes 3和5在这里是相关的)。如果你有一片a[i:j:k]:

  • If i or j are negative, they refer to an index from the end of a (so a[-1] refers to the last element of a)
  • 如果i或j是负数,它们表示a末尾的索引(因此a[-1]表示a的最后一个元素)
  • If i or j are not specified, or are None, they default to the ends of a, but which ends depends on the sign of k:

    如果i或j未被指定,或为None,则它们默认为a的端点,但其终止取决于k的符号:

    • if k is positive, you're slicing forwards, so i becomes 0 and j becomes len(a)
    • 如果k是正的,你向前切,所以i变成了0 j变成了len(a)
    • if k is negative, you're slicing backwards, so i becomes len(a) and j becomes the element before the start of a.

      如果k是负的,你在向后切割,所以我变成了len(a) j变成了元素在a开始之前。

      NB: j cannot be replaced with -1, since doing that will cause Python to treat j as the last element of a rather than the (nonexistent) element before a[0]. To get the desired behaviour, you must use -len(a)-1 (or -(len(a)+1)) in place of j, which means that to get to a[j], slice starts at the last element of a, goes left for len(a) elements and then left one more element, ending up before a starts and thus including a[0] in the slice.

      不能用-1替换j,因为这样做会导致Python将j视为a的最后一个元素,而不是[0]之前的(不存在的)元素。所需的行为,你必须使用len(a)1(或-(len(a)+ 1))的j,这意味着一个[j],切片的最后一个元素开始,就留给len(a)元素,然后留下一个元素,结束前开始,因此包括片[0]。

Therefore, a[:-1:-1] means "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 and j are equal – you start and stop slicing in the same place – so the expression evaluates to an empty list.

因此,a[:-1:-1]表示“从a的末尾a[-1](因为i未指定,k为负)到a的最后一个元素(因为j == -1),步长为-1”。i和j是相等的—您开始和停止在同一位置切片—因此表达式计算为一个空列表。

To reverse a[:-1], you can use a[-2::-1]. This way, the slice starts at the penultimate element, a[-2] (since a[:-1] does not include a[-1]) and goes backwards until the element "before" a[0], meaning that a[0] is included in the slice.

要反转a[:-1],你可以用a[-2:::-1]。这样,片从倒数第二个元素a[-2](因为a[:-1]不包含[-1])开始,一直向后直到元素“before”a[0],这意味着一个[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]

#5


1  

slice works similar to range in that when you make the step argument a negative number, the start and stop arguments work in the opposite direction.

slice的工作原理与range类似,当你将step参数设置为负数时,start和stop参数的工作方向是相反的。

>>> list(range(9, -1, -1)) == a[::-1]
True

Some examples that may help make this more clear:

一些例子可以帮助我们更清楚地了解这一点:

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

#1


21  

The first -1 in a[:-1:-1] doesn't mean what you think it does.

a[:-1:-1]中的第一个-1并不是你认为的那样。

In slicing, negative start/end indices are not interpreted literally. Instead, they are used to conveniently refer to the end of the list (i.e. they are relative to len(a)). This happens irrespectively of the direction of the slicing.

在切片中,负的起始/结束索引不是逐字解释的。相反,它们被用来方便地引用列表的末尾(即它们相对于len(a))。这与切片的方向无关。

This means that

这意味着

a[:-1:-1]

is equivalent to

相当于

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

When omitted during reverse slicing, the start index defaults to len(a)-1, making the above equivalent to

当在反向切片过程中被忽略时,开始索引默认为len(a)-1,使上面的操作等价于

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

This always gives an empty list, since the start and end indices are the same and the end index is exclusive.

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

To slice in reverse up to, and including, the zeroth element you can use any of the following notations:

要对零元素进行反向切片,您可以使用以下任何一种表示法:

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

#2


6  

When you type [1, 2, 3, ...][1:4:1] it is the same as [1, 2, 3, ...][slice(1, 4, 1)]. So 1:4:1 is the shorthand for slice object. slice signature is slice(stop) or slice(start, stop[, step]) and you can also use None for arguments.

当你键入[1,2,3,…[1:4:1]和[1,2,3,…][片(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

Suppose we have got [a: b: c]. Rules for indices will be as follows:

假设我们有[a: b: c]。指数规则如下:

  1. First c is checked. Default is +1, sign of c indicates forward or backward direction of the step. Absolute value of c indicates the step size.
  2. 首先检查c。默认为+1,c的符号表示步骤的向前或向后方向。c的绝对值表示步长。
  3. Than a is checked. When c is positive or None, default for a is 0. When c is negative, default for a is -1.
  4. 检查。当c为正或无时,a的默认值为0。当c为负时,a的默认值是-1。
  5. Finally b is checked. When c is positive or None, default for b is len. When c is negative default for b is -(len+1).
  6. 最后检查。当c为正或无时,b的默认值为len。当c为负时,b为-(len+1)

Note 1: Degenerated slices in Python are handled gracefully:

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

  • the index that is too large or too small is replaced with len or 0.
  • 索引太大或太小将被len或0替换。
  • an upper bound smaller than the lower bound returns an empty list or string or whatever else (for positive c).
  • 一个小于下界的上界返回一个空的列表或字符串或其他任何东西(对于正c)。

Note 2: Roughly speaking, Python picks up elements while this condition (a < b) if (c > 0) else (a > b) is True (updating a += c on every step). Also, all negative indices are replaced with len - index.

注2:粗略地说,Python在这个条件(a < b)的情况下(c > 0)是True(在每个步骤中更新a += c)。而且,所有的负指标都被len - index所取代。

If you combine this rules and notes it will make sense why you got an empty list. In your case:

如果你把这些规则和注释结合起来,你就会明白为什么你会得到一个空列表。在你的例子:

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

There is very good discussion about slice notation: Explain Python's slice notation!

关于切片表示法有很好的讨论:解释Python的切片表示法!

#3


4  

I generally find it useful to slice a range-object (this is only possible in python3 - in python2 range produces a list and xrange can't be sliced) if I need to see which indices are used for a list of a given length:

如果我需要查看给定长度的列表使用的是哪个索引,我通常会发现切片一个范围对象是有用的(这在python3中是唯一可能的——在python2中,范围产生一个列表,而xrange不能被分割):

>>> range(10)[::-1]  
range(9, -1, -1)

>>> range(10)[:-1]  
range(0, 9)

And in your last case:

最后一个例子是

>>> range(10)[:-1:-1]
range(9, 9, -1)

This also explains what happened. The first index is 9, but 9 isn't lower than the stop index 9 (note that in python the stop index is excluded) so it stops without giving any element.

这也解释了发生了什么。第一个索引是9,但是9不低于停止索引9(注意在python中停止索引被排除),所以它停止时不提供任何元素。

Note that indexing can also be applied sequentially:

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

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


1  

Python's slices seem fairly simple at first, but their behaviour is actually quite complex (notes 3 and 5 are relevant here). If you have a slice a[i:j:k]:

首先,Python的切片看起来相当简单,但是它们的行为实际上相当复杂(notes 3和5在这里是相关的)。如果你有一片a[i:j:k]:

  • If i or j are negative, they refer to an index from the end of a (so a[-1] refers to the last element of a)
  • 如果i或j是负数,它们表示a末尾的索引(因此a[-1]表示a的最后一个元素)
  • If i or j are not specified, or are None, they default to the ends of a, but which ends depends on the sign of k:

    如果i或j未被指定,或为None,则它们默认为a的端点,但其终止取决于k的符号:

    • if k is positive, you're slicing forwards, so i becomes 0 and j becomes len(a)
    • 如果k是正的,你向前切,所以i变成了0 j变成了len(a)
    • if k is negative, you're slicing backwards, so i becomes len(a) and j becomes the element before the start of a.

      如果k是负的,你在向后切割,所以我变成了len(a) j变成了元素在a开始之前。

      NB: j cannot be replaced with -1, since doing that will cause Python to treat j as the last element of a rather than the (nonexistent) element before a[0]. To get the desired behaviour, you must use -len(a)-1 (or -(len(a)+1)) in place of j, which means that to get to a[j], slice starts at the last element of a, goes left for len(a) elements and then left one more element, ending up before a starts and thus including a[0] in the slice.

      不能用-1替换j,因为这样做会导致Python将j视为a的最后一个元素,而不是[0]之前的(不存在的)元素。所需的行为,你必须使用len(a)1(或-(len(a)+ 1))的j,这意味着一个[j],切片的最后一个元素开始,就留给len(a)元素,然后留下一个元素,结束前开始,因此包括片[0]。

Therefore, a[:-1:-1] means "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 and j are equal – you start and stop slicing in the same place – so the expression evaluates to an empty list.

因此,a[:-1:-1]表示“从a的末尾a[-1](因为i未指定,k为负)到a的最后一个元素(因为j == -1),步长为-1”。i和j是相等的—您开始和停止在同一位置切片—因此表达式计算为一个空列表。

To reverse a[:-1], you can use a[-2::-1]. This way, the slice starts at the penultimate element, a[-2] (since a[:-1] does not include a[-1]) and goes backwards until the element "before" a[0], meaning that a[0] is included in the slice.

要反转a[:-1],你可以用a[-2:::-1]。这样,片从倒数第二个元素a[-2](因为a[:-1]不包含[-1])开始,一直向后直到元素“before”a[0],这意味着一个[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]

#5


1  

slice works similar to range in that when you make the step argument a negative number, the start and stop arguments work in the opposite direction.

slice的工作原理与range类似,当你将step参数设置为负数时,start和stop参数的工作方向是相反的。

>>> list(range(9, -1, -1)) == a[::-1]
True

Some examples that may help make this more clear:

一些例子可以帮助我们更清楚地了解这一点:

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