Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。
另请参见:为什么切片和范围上限是互斥的?
当前回答
这是我教新手切片的方法:
理解索引和切片之间的区别:
WikiPython有一幅惊人的图片,它清楚地区分了索引和切片。
这是一个包含六个元素的列表。为了更好地理解切片,请将该列表视为一组放在一起的六个框。每个盒子里都有一个字母表。
索引就像处理盒子的内容。您可以检查任何框的内容。但是你不能同时检查多个盒子的内容。你甚至可以替换盒子里的东西。但你不能在一个盒子里放两个球,也不能一次换两个球。
In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']
In [124]: alpha[0]
Out[124]: 'a'
In [127]: alpha[0] = 'A'
In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']
In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]
TypeError: list indices must be integers, not tuple
切片就像处理盒子一样。你可以拿起第一个盒子放在另一张桌子上。要拿起盒子,你只需要知道盒子的开始和结束位置。
您甚至可以选择前三个框或最后两个框,或1到4之间的所有框。所以,如果你知道开始和结束,你可以选择任何一组框。这些位置称为开始和停止位置。
有趣的是,您可以同时替换多个框。此外,您可以在任何地方放置多个盒子。
In [130]: alpha[0:1]
Out[130]: ['A']
In [131]: alpha[0:1] = 'a'
In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']
In [133]: alpha[0:2] = ['A', 'B']
In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']
In [135]: alpha[2:2] = ['x', 'xx']
In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']
切片步骤:
到目前为止,您已连续拾取箱子。但有时你需要单独拾取。例如,您可以每隔一秒钟拾取一个盒子。你甚至可以从最后每隔三个盒子取一个。该值称为步长。这代表了您连续拾取之间的差距。如果您从开始到结束拾取框,则步长应为正值,反之亦然。
In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']
In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']
In [144]: alpha[1:5:-2]
Out[144]: []
In [145]: alpha[-1:-5:2]
Out[145]: []
Python如何找出缺少的参数:
切片时,如果忽略了任何参数,Python会尝试自动计算。
如果您检查CPython的源代码,您会发现一个名为PySlice_GetIndices Ex()的函数,它计算出任何给定参数的切片索引。下面是Python中的逻辑等价代码。
此函数采用Python对象和可选参数进行切片,并返回所请求切片的开始、停止、步骤和切片长度。
def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):
length = len(obj)
if step is None:
step = 1
if step == 0:
raise Exception("Step cannot be zero.")
if start is None:
start = 0 if step > 0 else length - 1
else:
if start < 0:
start += length
if start < 0:
start = 0 if step > 0 else -1
if start >= length:
start = length if step > 0 else length - 1
if stop is None:
stop = length if step > 0 else -1
else:
if stop < 0:
stop += length
if stop < 0:
stop = 0 if step > 0 else -1
if stop >= length:
stop = length if step > 0 else length - 1
if (step < 0 and stop >= start) or (step > 0 and start >= stop):
slice_length = 0
elif step < 0:
slice_length = (stop - start + 1)/(step) + 1
else:
slice_length = (stop - start - 1)/(step) + 1
return (start, stop, step, slice_length)
这就是切片背后的智慧。由于Python有一个名为slice的内置函数,您可以传递一些参数,并检查它如何巧妙地计算缺少的参数。
In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [22]: s = slice(None, None, None)
In [23]: s
Out[23]: slice(None, None, None)
In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)
In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]
In [26]: s = slice(None, None, -1)
In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]
In [28]: s = slice(None, 3, -1)
In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]
注:这篇文章最初写在我的博客《Python切片背后的智能》中。
其他回答
如果你觉得切片中的负指数令人困惑,这里有一个非常简单的方法来考虑:用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)]
这里有一个简单的记忆方法,可以记住它是如何工作的:
S L*I*C*E*切片的“i”位于第一位,代表包容,“e”排在最后,代表独占。
所以array[j:k]将包括第j个元素,并排除第k个元素。
语法为:
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()对象的显式使用简化了切片的编程生成。
您可以使用切片语法返回字符序列。
指定用冒号分隔的开始和结束索引,以返回字符串的一部分。
例子:
获取从位置2到位置5的字符(不包括):
b = "Hello, World!"
print(b[2:5])
从开始切片
通过省略起始索引,范围将从第一个字符开始:
例子:
获取从开始到位置5的字符(不包括):
b = "Hello, World!"
print(b[:5])
切片到底
通过省略结束索引,范围将结束:
例子:
从位置2获取字符,一直到结尾:
b = "Hello, World!"
print(b[2:])
负索引
使用负索引从字符串末尾开始切片:实例
获取字符:
来自:“世界!”中的“o”(位置-5)
至,但不包括:“世界!”中的“d”(位置-2):
b = "Hello, World!"
print(b[-5:-2])
基本的切片技术是定义起点、终点和步长(也称为步幅)。
首先,我们将创建一个用于切片的值列表。
创建两个要切片的列表。第一个是从1到9的数字列表(列表a)。第二个也是数字列表,从0到9(列表B):
A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))
print("This is List A:", A)
print("This is List B:", B)
索引A中的数字3和B中的数字6。
print(A[2])
print(B[6])
基本切片
用于切片的扩展索引语法是aList[start:stop:step]。start参数和step参数都默认为None,唯一需要的参数是stop。您是否注意到这与使用范围定义列表A和B的方式类似?这是因为切片对象表示由范围(开始、停止、步骤)指定的索引集。
如您所见,仅定义stop返回一个元素。由于start默认为none,这意味着只检索一个元素。
需要注意的是,第一个元素是索引0,而不是索引1。这就是为什么我们在本练习中使用两个列表。列表A的元素根据序号位置进行编号(第一个元素是1,第二个元素是2,等等),而列表B的元素是用于对其进行索引的数字(对于第一个元素,[0],等等)。
通过扩展索引语法,我们可以检索一系列值。例如,使用冒号检索所有值。
A[:]
要检索元素的子集,需要定义开始和停止位置。
给定模式aList[start:stop],从列表A中检索前两个元素。