Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。


另请参见:为什么切片和范围上限是互斥的?


当前回答

在Python中,最基本的切片形式如下:

l[start:end]

其中l是一些集合,start是一个包含索引,end是一个独占索引。

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

当从开始切片时,可以省略零索引,而当切片到结束时,可以忽略最终索引,因为它是冗余的,所以不要冗长:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

负整数在相对于集合结尾进行偏移时非常有用:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

切片时可以提供超出范围的索引,例如:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

请记住,分割集合的结果是一个全新的集合。此外,当在赋值中使用切片表示法时,切片赋值的长度不需要相同。将保留分配切片之前和之后的值,集合将收缩或增长以包含新值:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

如果忽略开始索引和结束索引,则将创建集合的副本:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

如果在执行赋值操作时省略了开始和结束索引,则集合的整个内容将替换为引用内容的副本:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

除了基本切片外,还可以应用以下符号:

l[start:end:step]

其中l是一个集合,start是一个包含索引,end是一个排他索引,step是一个步长,可以用来获取l中的每n个项目。

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

使用step提供了在Python中反转集合的有用技巧:

In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

也可以使用负整数作为步骤,如下例所示:

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

然而,使用负值作为步长可能会变得非常混乱。此外,为了成为Pythonic,您应该避免在单个切片中使用start、end和step。如果需要这样做,可以考虑在两个任务中完成(一个任务是切片,另一个任务则是跨步)。

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]

其他回答

语法为:

a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array

还有一个步长值,可用于上述任何一项:

a[start:stop:step] # start through not past stop, by step

要记住的关键点是:stop值表示不在所选切片中的第一个值。因此,停止和开始之间的区别是所选元素的数量(如果步骤为1,则为默认值)。

另一个特点是start或stop可以是负数,这意味着它从数组的末尾开始计数,而不是从开始计数。因此:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

类似地,步骤可以是负数:

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

如果项目比你要求的少,Python对程序员很友好。例如,如果您请求一个[:-2],而一个只包含一个元素,则会得到一个空列表而不是一个错误。有时你会更喜欢错误,所以你必须意识到这可能会发生。

与切片对象的关系

切片对象可以表示切片操作,即:

a[start:stop:step]

相当于:

a[slice(start, stop, step)]

根据参数的数量,切片对象的行为也略有不同,类似于range(),即切片(stop)和切片(start,stop[,step])都受支持。要跳过指定给定参数,可以使用None,例如[start:]等同于[sslice(start,None)]或[::-1]等同于[Sslice(None,None,-1)]。

虽然基于:的表示法对简单切片非常有用,但slice()对象的显式使用简化了切片的编程生成。

这只是为了一些额外的信息。。。考虑以下列表

>>> l=[12,23,345,456,67,7,945,467]

反转列表的其他几个技巧:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

在找到这张很棒的桌子http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

在我看来,如果您按照以下方式查看Python字符串切片表示法(继续阅读),您将更好地理解和记忆它。

让我们使用以下字符串。。。

azString = "abcdefghijklmnopqrstuvwxyz"

对于不知道的人,可以使用符号azString[x:y]从azString创建任何子字符串

来自其他编程语言,这是常识受到损害的时候。x和y是什么?

为了寻找一种记忆技巧,我不得不坐下来运行了几个场景,帮助我记住x和y是什么,并帮助我在第一次尝试时正确地切分字符串。

我的结论是,x和y应该被视为围绕我们想要增加的字符串的边界索引。因此,我们应该将表达式视为azString[index1,index2],或者更清晰地视为azString[index_of_first_charactere,index_after_the_last_character]。

这是一个可视化的例子。。。

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

因此,您所要做的就是将index1和index2设置为所需子字符串周围的值。例如,要获得子字符串“cdefgh”,可以使用azString[2:8],因为“c”左侧的索引是2,而“h”右侧的索引是8。

请记住,我们正在设置边界。这些边界是可以放置一些括号的位置,括号将像这样围绕子字符串。。。

a b[c d e f g h]i j公司

这个技巧一直有效,而且很容易记住。

Python切片表示法:

a[start:end:step]

对于开始和结束,负值被解释为相对于序列的结束。结束的正索引表示要包含的最后一个元素之后的位置。空白值默认如下:[+0:-0:1]。使用否定步骤会颠倒开始和结束的解释

该符号扩展到(numpy)矩阵和多维数组。例如,要分割整个列,可以使用:

m[::,0:2:] ## slice the first two columns

切片保存数组元素的引用,而不是副本。如果您想单独复制一个数组,可以使用deepcopy()。