如何通过切片符号 a[::-1] 解释序列的反转

Posted

技术标签:

【中文标题】如何通过切片符号 a[::-1] 解释序列的反转【英文标题】:How to explain the reverse of a sequence by slice notation a[::-1] 【发布时间】:2017-11-07 05:36:43 【问题描述】:

来自python.org tutorial

切片索引有有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为被切片的字符串的大小。

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

正如教程所说,a[::-1] 应该等于 a[0:5:-1]

a[0:5:-1]为空如下:

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

该问题与explain-slice-notation 不重复。这个问题是关于 python 中切片的一般用途。

【问题讨论】:

@Jean-FrançoisFabre >>> print(a[5:0:-1]) olle 文档没有提到反向迭代的情况,是的。好点子。 a[5::-1]怎么样 Explain slice notation的可能重复 @juanpa.arrivillaga 权力越大,责任越大:你刚刚发现python金徽章可以单手关闭/重新打开:) 【参考方案1】:

我认为文档可能对此有点误导,但如果省略切片的可选参数与使用 None 相同:

>>> a = "hello"
>>> a[::-1]
'olleh'
>>> a[None:None:-1]
'olleh'

您可以从 CPython 字节码中看到上面的这 2 个切片是相同的:

>>> 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) - 1start-len(a) - 1end

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

这可以帮助您将其可视化:

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

【讨论】:

你能给the substituted values for None are len(a) - 1 for the start and -len(a) - 1 for the end一些参考 @Alex "当 k 为负数时,如果 i 和 j 更大,则将它们缩减为 len(s) - 1。如果 i 或 j 被省略或 None,它们将成为“结束”值(即结束取决于 k) 的符号”见第 5 点。docs.python.org/3/library/… @Alex 另请参阅这个相关的 SO 问题:***.com/questions/12521798/…【参考方案2】:

您对步进的行为感到困惑。为了得到同样的结果,你可以做的是:

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

确实,在您的情况下,stepping 想要向后“绕圈”,但您通过调用 a[0:5:-1] 来限制它的移动。

【讨论】:

我不同意这个解释,hello'[0:5:1] 会返回hello,那为什么-1 会计算为'' 因为步进根本不切片,只是步进。 不清楚你的意思,我个人认为这是语言不一致。【参考方案3】:

它所做的只是slice。你选。 start stop and step 所以基本上你是说它应该从头开始直到开始但向后 (-1)。

如果你用 -2 做它会跳过字母:

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

[0:5:-1] your'e 从第一个字母开始并直接返回到 5 时,它将停止。只有你尝试[-1::-1],它才能正确地通过负1的步骤进入开头。

编辑回答 cmets

正如documentation 所说的那样

省略的第二个索引默认为字符串的大小 切片。

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

另外,this answer 很棒

【讨论】:

我确信 OP 知道 a[::-1] 做了什么。 教程中说“a[::-1]省略的第二个索引应该是字符串的大小”,所以a[::-1]等于a[:5:-1 ]。为什么说“它应该从头开始直到开始”?我是不是误会了? @IsaacDj教程链接docs.python.org/2/tutorial/introduction.html如下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.【参考方案4】:

a[0:5:-1] 没有多大意义,因为当您使用此符号时,索引表示:a[start:end:step]。当您使用负步长时,您的最终值需要位于比起始值“更早”的位置。

【讨论】:

【参考方案5】:

您会注意到第三个切片参数step 并未在您引用的教程部分中提供。那个特定的 sn-p 假设是一个积极的步骤。

当您添加负步骤的可能性时,该行为实际上非常直观。一个空的start 参数是指从序列的任何一端开始沿着step 值指示的方向逐步遍历整个序列。换句话说,如果您有一个正步骤,它指的是最低索引(向上计数),如果您有一个负步骤,它指的是最高索引(向下计数)。同样,一个空的end 参数指的是在沿着适当的方向单步执行之后将结束的序列的任何一端。

【讨论】:

【参考方案6】:

正如您所指出的,文档中的默认值根本不正确。但是,除了那个小错误之外,它们是一致的。您可以在此处查看我所指的文档:https://docs.python.org/3/library/stdtypes.html#common-sequence-operations

请注意,根据文档,该行为在定义上是正确的:

从 i 到 j 步长为 k 的 s 切片定义为 具有索引 x = i + n*k 的项目,使得 0

当你这样做时:

>>> 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,所以你得到的是空字符串:

>>> y
''

如有疑问,请使用a[start:stop][::step](这几乎总是我们想要的)

几乎总是这样,当您将负步骤传递给 x[start:stop:step] 之类的东西时,您想要发生的事情是首先发生子选择,然后通过 @ 向后退987654336@(即我们通常想要x[start:stop][::step]

此外,更令人困惑的是,

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

如果step > 0。例如:

>>> 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 切片之后执行步骤子句 会更安全。所以我几乎总是y = x[start:stop][::step] 开始,至少在设计原型或创建以正确性/可读性为主要关注点的新模块时。这比做单个切片的性能要差,但如果性能是一个问题,那么你可以做可读性差的事情:

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

HTH。

【讨论】:

【参考方案7】:

对于 sequence[start:stop:step] 的 Python 切片,已导出以下规则:

    开始:停止 = 开始:停止:1 start:stop:(+ or -) step - 表示遍历时跳过序列中的 N 个项目。但是,(-)表示向后遍历 记住,序列中最后一项的位置是-1,前一项是-2,以此类推..

# start:stop: +step 规则

    始终向前遍历 始终从序列的开头开始作为其积极的一步(前进) 从请求位置开始,在请求位置停止但不包括项目停止位置 默认开始:如果没有提供开始,则从 0 开始 默认停止:如果没有提供停止,则表示直到序列结束包括最后一个值 如果停止位置的项目不可到达(项目超出序列遍历的末尾),切片不返回任何内容

# start:stop:-step 规则

    总是反向遍历 如果提供了起始位置,则从那里开始,但反向遍历(它是后退一步) 如果提供了停止,则停止遍历,但排除此 默认开始:如果没有提供开始位置,则开始位置是序列的最后一个位置(因为负遍历) 默认停止:如果未提供停止,则为列表的开头(位置 0) 如果停止位置的项目不可到达(项目超出序列遍历的末尾),切片不返回任何内容

【讨论】:

以上是关于如何通过切片符号 a[::-1] 解释序列的反转的主要内容,如果未能解决你的问题,请参考以下文章

一文读懂网络切片

彻底搞懂python序列的切片操作

python序列

反转 UTF-8 编码的字符串

理解切片符号

在Python中反转一个字符串