Python中的字符串扩展切片

4
>>>"helloworld"[::1]
'helloworld'

>>>"helloworld"[::-1]
'dlrowolleh'

根据语法 str[start:end:step],默认情况下两种情况下的起始位置都是0。在第一种情况下,字符串从索引值0开始打印。但是在第二种情况下,字符串从索引值-1开始打印。我的问题是为什么后一种情况下字符串从-1开始打印,为什么会这样?

它从0开始,这位于最后一个字符和第一个字符之间。 - njzk2
7个回答

8
根据文档(重点添加):
对于步长为k的s中从i到j的切片,定义为索引x = i + n * k的项目序列,其中0 <= n <(j-i)/k。换句话说,索引为i,i + k,i + 2 * k,i + 3 * k等,在达到j时停止(但不包括j)。如果i或j大于len(s),则使用len(s)。如果省略了i或j,则它们成为“end”值(结束取决于k的符号)。请注意,k不能为零。如果k为None,则将其视为1。
这意味着,如果切片步幅为正数,则省略的切片开始是序列的开头,省略的切片结束是序列的结尾。如果切片步幅为负,则相反。如果您填写两个值中的一个,就可以看到这一点:
>>> '0123456'[:3]
'012'
>>> '0123456'[:3:-1]
'654'
>>> '0123456'[3:]
'3456'
>>> '0123456'[3::-1]
'3210'

一种思考方式是将序列想象为一个循环,起点和终点是同一个点。当您省略切片的一端时,您只是指定使用这个“两端点”作为端点,但没有指定从哪里开始走。步幅符号告诉您应该往哪个方向走,这决定了您将“两端点”视为序列的开头还是结尾。

BrenBarn 根据您的说法,如果省略了 i 和 j,或者它们不存在,则它们将成为结束值,并且取决于 k 的符号选择哪个末端。如果您不介意,可以再解释一下吗? - user2466595
@KrishanAggarwal:我已经在我的回答的下一部分解释了它(即“这意味着…”部分)。 - BrenBarn

2

扩展切片组件默认全部为None(与简单切片的0和sys.maxint相对):

>>> class A:
...   def __getitem__(self, s):
...     return s
... 
>>> A()[::-1]
slice(None, None, -1)
>>> A()[:]
slice(0, 9223372036854775807, None)

因此,并没有自动假设切片默认从零开始的前提。

1

形象化一下

记住切片的最好方法是将索引视为指向字符之间的位置,其中第一个字符的左边缘编号为0。然后,n个字符的字符串的最后一个字符的右边缘具有索引n,例如:

 +---+---+---+---+---+ 
 | H | e | l | l | o |
 +---+---+---+---+---+ 
 0   1   2   3   4   5 
-5  -4  -3  -2  -1

索引可能是负数,以从右侧开始计数。但请注意,-0实际上与0相同,因此不会从右侧计数!

In [105]: "helloworld"[-0] 
Out[105]: 'h'

In [106]: "helloworld"[0]
Out[106]: 'h'

即为什么反向索引从-1开始

In [107]: "helloworld"[-1] 
Out[107]: 'd'

要获取字符串的倒数第二个索引,即[-2],也就是倒数第二个字符,需要使用负步长来到达下一个索引。

In [108]: "helloworld"[-1 + -1]
Out[108]: 'l'

0
在Python中,字符串的索引如下所示。
"H e l l o"
0 1 2 3 4
-4 -3 -2 -1 0

将要使用的索引取决于您正在获取的切片方向。因为您提供的步长是相反方向,它使用下面的索引。然而,在文档中并没有明确说明这一点。

编辑:

我实际上重新检查了,有趣的是

str[::-1]
str[0::-1]
str[-1::-1]

所有返回相同的值。因此,我在原帖中所说的似乎是错误的。它看起来更像是语言中的一个错误或特殊情况处理。


在Python 2.7中,'hello'[0::-1]的求值结果为'h'。 - Marcelo Cantos

0
你所看到的是被称为“striding”:
>>> 'helloworld'[::1]

返回所有元素。
>>> 'helloworld'[::2]
'hlool'

返回每2个元素。现在尝试:

>>> 'helloworld'[::-2]
'drwle'

这将返回从末尾开始的每2个元素。因此,自然而然地,从末尾开始的所有元素就是反转的字符串:

>>> 'helloworld'[::-1]
'dlrowolleh'

0

如果反转字符串时起始位置不是隐式的-1,那么就没有意义。如果您尝试使用显式索引,您会发现当使用-1作为步长时,起始索引必须在结束索引的右侧:

>>> "helloworld"[0:-1:-1]
''
>>> "helloworld"[-1:0:-1]
'dlrowolle'

正如普通切片时一样,范围包括起始点但不包括结束点,因此索引0处的h不是范围的一部分。据我所知,这是切片符号的一个限制,因为无法执行整个字符串的明确反转,因为以下操作无法实现:

>>> "helloworld"[-1:-1:-1]
''

因此,一个切片和反转函数必须针对这种情况进行特殊处理:

def slice_and_reverse(s, a, b):
    "Return a slice of s from a to but not including b, reversed."
    if a == 0:
        return s[b - 1::-1]
    else:
        return s[b - 1:a - 1:-1]

0

当步长为1时,您将获得原始字符串,这并不奇怪。

当步长为-1时,Python可能会实现一个特殊情况:反转顺序。

毕竟,切片[start:end],如[:]返回完整的切片,这是预期的行为。因此,将其视为两个阶段的操作:获取切片(在您的情况下是完全复制),然后应用步幅(在您的情况下是反向)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接