关于python:如何通过切片符号a [:: – 1]来解释序列的反转

How to explain the reverse of a sequence by slice notation a[::-1]

从python.org教程

Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.

1
2
3
>>> a ="hello"
>>> print(a[::-1])
olleh

应该说,作为一a[0:5:-1]equals a[::-1]教程

a[0:5:-1]is empty为如下:

1
2
>>> print(len(a[0:5:-1]))
0

这样的问题是没有解释的重复(多层的符号。这是一个总在Python中使用切片。


我认为文档在这方面可能有点误导性,但是如果省略切片的可选参数与使用None相同:

1
2
3
4
5
>>> a ="hello"
>>> a[::-1]
'olleh'
>>> a[None:None:-1]
'olleh'

您可以看到上面的两个切片与cpython字节码完全相同:

1
2
3
4
5
6
7
8
9
>>> import dis
>>> dis.dis('a[::-1]') # or dis.dis('a[None:None:-1]')
  1           0 LOAD_NAME                0 (a)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 LOAD_CONST               2 (-1)
             12 BUILD_SLICE              3
             15 BINARY_SUBSCR
             16 RETURN_VALUE

对于负的stepNone的替换值为len(a) - 1代表start-len(a) - 1代表end

1
2
3
4
5
6
>>> a[len(a)-1:-len(a)-1:-1]
'olleh'
>>> a[4:-6:-1]
'olleh'
>>> a[-1:-6:-1]
'olleh'

这可能有助于您将其可视化:

1
2
3
    h  e  l  l  o  
    0  1  2  3  4  5
-6 -5 -4 -3 -2 -1


你对脚步的行为感到困惑。要得到相同的结果,您可以做的是:

1
2
a[0:5][::-1]
'olleh'

实际上,在你的例子中,stepping想要向后"循环",但是你通过调用a[0:5:-1]来限制它的移动。


它所做的就是切片。你挑吧。开始,停止,走,所以基本上你是说,它应该从一开始就开始,直到开始,但是向后走(-1)。

如果使用-2,它将跳过字母:

1
2
>>> a[::-2]
'olh'

当你做[0:5:-1]时,你的E从第一个字母开始,直接回到5,这样它就会停止。只有当你尝试了EDOCX1[1]时,它才能正确地通过执行负1的步骤开始。

编辑以答复评论

正如文件所指出的那样

an omitted second index defaults to the size of the string being
sliced.

假设我们有strlen(str) = 5。当您对字符串进行切片并省略时,去掉第二个数字,它默认为要切片的字符串的长度,在本例中是-5。即str[1:] == str[1:5]str[2:] == str[2:5]。句子指的是原始对象的长度,而不是新切片对象的长度。

而且,这个答案很好


a[0:5:-1]没有多大意义,因为当使用这个符号时,指数的意思是:a[start:end:step]。当您使用负阶跃时,您的最终值需要处于比初始值"更早"的位置。


您会注意到第三个slice参数step没有在您引用的教程部分中出现。这个特定的片段假定是一个积极的步骤。

当你加入消极步骤的可能性时,行为实际上是非常直观的。一个空的start参数指的是序列的任何一端,即按照step值指示的方向,从该端开始逐步执行整个序列。换句话说,如果你有一个积极的步骤,它指的是最低的索引(向上计数),如果你有一个消极的步骤,它指的是最高的索引(向下计数)。同样,一个空的end参数指的是序列的任何一端,在沿着适当的方向通过之后,序列的任何一端都会结束。


对于序列[start:stop:step]的python切片,派生了以下规则:

  • 开始:停止=开始:停止:1
  • 开始:停止:(+或-)步骤-当遍历序列中的n个项目时。然而,(-)表示反向遍历
  • 记住,最后一项的顺序位置是-1,而之前一项的顺序是-2,依此类推。
  • #开始:停止:+步骤规则

  • 始终向前移动
  • 始终从序列的开始作为其积极步骤(前进)
  • 从请求的位置开始,在请求的位置停止,但不包括项目停止位置
  • 默认开始:如果未提供开始,则从0开始
  • 默认停止:如果不提供停止,则表示直到序列结束,包括最后一个值
  • 如果停止位置的项不可访问(项超出序列遍历的结尾),则slice不返回任何内容。
  • #开始:停止:步骤规则

  • 始终反向移动
  • 如果提供了起始位置,则从该位置开始,但反向横移(向后退一步)
  • 如果提供了停止,则停止在那里遍历,但不包括此
  • 默认开始:如果没有提供开始位置,则开始位置是序列的最后一个位置(因为反向遍历)
  • 默认停止:如果不提供停止,则它是列表的开始(位置0)
  • 如果停止位置的项不可访问(项超出序列遍历的结尾),则slice不返回任何内容。

  • 正如您所指出的,文档对于默认值是不正确的。但是,除了那个小错误,它们是一致的。您可以在这里查看我引用的文档:https://docs.python.org/3/library/stdtypes.html公共序列操作

    请注意,根据文档,行为是绝对正确的:

    The slice of s from i to j with step k is defined as the sequence of
    items with index x = i + n*k such that 0 <= n < (j-i)/k. In other words, the indices are i, i+k, i+2*k, i+3*k and so on, stopping when j is reached (but never including j).

    当你这样做的时候:

    1
    2
    >>> a ="hello"
    >>> y = a[0:5:-1]

    我们有i == 0j == 5k == -1。因此,我们在指数x = i + n*k上为n抓取项目,从0开始,到(j-i)/k。然而,观察(j-i)/k == (5-0)/-1 == -5。没有n这样的0 <= n < -5所以得到空字符串:

    1
    2
    >>> y
    ''

    号当有疑问时(这几乎总是我们想要的)做a[start:stop][::step]

    几乎所有的情况都是这样的,当你向像x[start:stop:step]这样的事情迈出消极的一步时,你希望发生的事情是先进行子选择,然后再通过step向后(即我们通常希望x[start:stop][::step])。

    此外,更令人困惑的是,事实上

    1
    x[start:stop:step] == x[start:stop][::step]

    如果是step > 0。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> x = list(range(10))
    >>> x
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> x[2:6:2]
    [2, 4]
    >>> x[2:6][::2]
    [2, 4]
    >>> x[1:10][::3]
    [1, 4, 7]
    >>> x[1:10:3]
    [1, 4, 7]

    不幸的是,当step < 0时,这并不适用,尽管它很容易让人认为应该这样做。

    经过几次这样的处理,我意识到在执行start:stop切片之后总是执行step子句比较安全。因此,我几乎总是从y = x[start:stop][::step]开始,至少在原型设计或创建一个新模块时,正确性/可读性是首要问题。这比执行单个切片的性能差,但是如果性能是一个问题,那么您可以执行可读性差的操作:

    1
    y = x[start:stop:step] if step > 0 else x[stop:start:step]

    Hth.