Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
当前回答
关于序列的索引,需要记住的重要思想是
非负指数从序列中的第一项开始;负索引从序列的最后一项开始(因此仅适用于有限序列)。
换言之,负索引右移序列长度:
0 1 2 3 4 5 6 7 ...
-------------------------
| a | b | c | d | e | f |
-------------------------
... -8 -7 -6 -5 -4 -3 -2 -1
考虑到这一点,订阅和切片很简单。
订阅
订阅使用以下语法:*
sequence[index]
订阅在索引处选择序列中的单个项目:
>>> 'abcdef'[0]
'a'
>>> 'abcdef'[-6]
'a'
订阅引发超出范围的索引的IndexError:
>>> 'abcdef'[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
切片
切片使用以下语法:**
sequence[start:stop:step]
切片选择序列中的一系列项目,从包含开始到不包含结束:
>>> 'abcdef'[0:2:1]
'ab'
>>> 'abcdef'[0:-4:1]
'ab'
>>> 'abcdef'[-6:-4:1]
'ab'
>>> 'abcdef'[-6:2:1]
'ab'
>>> 'abcdef'[1:-7:-1]
'ba'
>>> 'abcdef'[-5:-7:-1]
'ba'
切片默认为序列中最完整的项目范围,因此它对省略或等于“无”的任何开始、停止或步骤使用以下默认值:***
步骤默认为1;如果步骤为正start默认为0(第一项索引),stop默认为start+len(序列)(最后一项索引加一);如果步骤为负start默认为-1(最后一项索引),stop默认为start-len(序列)(第一项索引减一)。
>>> 'abcdef'[0:6:1]
'abcdef'
>>> 'abcdef'[::]
'abcdef'
>>> 'abcdef'[-1:-7:-1]
'fedcba'
>>> 'abcdef'[::-1]
'fedcba'
切片为等于零的步骤引发ValueError:
>>> 'abcdef'[::0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: slice step cannot be zero
切片不会为超出范围的开始或停止引发IndexError(与订阅相反):
>>> 'abcdef'[-100:100]
'abcdef'
*表达式sequence[index]和sequence__getitem_(索引)是等效的。
**表达式sequence[start:stop:step]、sequence[Sslice(start,stop,step)]和sequence__getitem_(slice(start,stop,step))是等效的,其中内置类slice实例包start,stop和step。
***表达式sequence[:]、sequence[::]和sequence[None:None:None]使用start、stop和step的默认值。
其他回答
在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]
Python切片表示法:
a[start:end:step]
对于开始和结束,负值被解释为相对于序列的结束。结束的正索引表示要包含的最后一个元素之后的位置。空白值默认如下:[+0:-0:1]。使用否定步骤会颠倒开始和结束的解释
该符号扩展到(numpy)矩阵和多维数组。例如,要分割整个列,可以使用:
m[::,0:2:] ## slice the first two columns
切片保存数组元素的引用,而不是副本。如果您想单独复制一个数组,可以使用deepcopy()。
上面的答案不讨论切片分配。为了理解切片分配,可以在ASCII艺术中添加另一个概念:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
Slice position: 0 1 2 3 4 5 6
Index position: 0 1 2 3 4 5
>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
'P'
>>> p[5]
'n'
# Slicing gives lists
>>> p[0:1]
['P']
>>> p[0:2]
['P','y']
一种启发式方法是,对于从零到n的切片,思考:“零是开始,从开始开始,在列表中取n个项目”。
>>> p[5] # the last of six items, indexed from zero
'n'
>>> p[0:5] # does NOT include the last item!
['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
['P','y','t','h','o','n']
另一种启发式方法是,“对于任何一个切片,用零替换开头,应用前面的启发式方法获得列表的结尾,然后将第一个数字向后计数,以从开头删除项目”
>>> p[0:4] # Start at the beginning and count out 4 items
['P','y','t','h']
>>> p[1:4] # Take one item off the front
['y','t','h']
>>> p[2:4] # Take two items off the front
['t','h']
# etc.
切片分配的第一个规则是,由于切片返回一个列表,所以切片分配需要一个列表(或其他可迭代的):
>>> p[2:3]
['t']
>>> p[2:3] = ['T']
>>> p
['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
切片分配的第二个规则(您也可以在上面看到)是,无论切片索引返回列表的哪个部分,都是由切片分配更改的相同部分:
>>> p[2:4]
['T','h']
>>> p[2:4] = ['t','r']
>>> p
['P','y','t','r','o','n']
切片分配的第三条规则是,分配的列表(可迭代)不必具有相同的长度;索引切片被简单地切片,并被分配的任何内容整体替换:
>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
['P','y','s','p','a','m','o','n']
最难习惯的部分是分配给空切片。使用启发式1和2,很容易让你的头脑围绕空切片进行索引:
>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]
然后,一旦您看到了这一点,将切片分配给空切片也是有意义的:
>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
['P','y','t','h','x','y','o','n'] # The result is longer still
请注意,因为我们没有更改切片的第二个编号(4),所以插入的项目总是紧靠“o”堆叠,即使我们分配给空切片也是如此。因此,空切片分配的位置是非空切片分配位置的逻辑扩展。
稍微后退一点,当你继续进行我们的切片开始计数过程时会发生什么?
>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]
>>> p[5:4]
[]
>>> p[6:4]
[]
通过切片,一旦你完成,你就完成了;它不会开始向后倾斜。在Python中,除非使用负数明确要求,否则不会获得负的步幅。
>>> p[5:3:-1]
['n','o']
“一旦你完成了,你就完成了”规则会产生一些奇怪的后果:
>>> p[4:4]
[]
>>> p[5:4]
[]
>>> p[6:4]
[]
>>> p[6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
事实上,与索引相比,Python切片具有奇怪的防错误性:
>>> p[100:200]
[]
>>> p[int(2e99):int(1e99)]
[]
这有时会派上用场,但也会导致一些奇怪的行为:
>>> p
['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']
根据您的应用程序,这可能。。。或者可能不。。。成为你在那里所希望的!
以下是我的原始答案。它对很多人都很有用,所以我不想删除它。
>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]
这也可以澄清切片和索引之间的区别。
语法为:
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()对象的显式使用简化了切片的编程生成。
Python教程对此进行了讨论(向下滚动一点,直到您了解到关于切片的部分)。
ASCII艺术图也有助于记住切片的工作方式:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
记住切片工作方式的一种方法是将索引视为字符之间的指针,第一个字符的左边缘编号为0。然后,n个字符串的最后一个字符的右边缘具有索引n。