Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
当前回答
使用一点后,我意识到最简单的描述是它与for循环中的参数完全相同。。。
(from:to:step)
其中任何一项都是可选的:
(:to:step)
(from::step)
(from:to)
然后,负索引只需要将字符串的长度添加到负索引中即可理解它。
不管怎样,这对我来说都很有效。。。
其他回答
上面的答案不讨论切片分配。为了理解切片分配,可以在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]
这也可以澄清切片和索引之间的区别。
在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]
我的大脑似乎很乐意接受lst[开始:结束]包含开始项。我甚至可以说这是一个“自然的假设”。
但偶尔会有一种怀疑悄悄出现,我的大脑会要求我保证它不包含结尾元素。
在这些时刻,我依靠这个简单的定理:
for any n, lst = lst[:n] + lst[n:]
这个漂亮的属性告诉我,lst[start:end]不包含end-th项,因为它位于lst[end:]中。
注意,这个定理对任何n都是正确的。例如,您可以检查
lst = range(10)
lst[:-42] + lst[-42:] == lst
返回True。
如果你觉得切片中的负指数令人困惑,这里有一个非常简单的方法来考虑:用len-index替换负指数。例如,用len(list)-3替换-3。
说明切片在内部做什么的最佳方法是在实现此操作的代码中显示它:
def slice(list, start = None, end = None, step = 1):
# Take care of missing start/end parameters
start = 0 if start is None else start
end = len(list) if end is None else end
# Take care of negative start/end parameters
start = len(list) + start if start < 0 else start
end = len(list) + end if end < 0 else end
# Now just execute a for-loop with start, end and step
return [list[i] for i in range(start, end, step)]
关于序列的索引,需要记住的重要思想是
非负指数从序列中的第一项开始;负索引从序列的最后一项开始(因此仅适用于有限序列)。
换言之,负索引右移序列长度:
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的默认值。