提问人:jtbradle 提问时间:1/30/2009 更新时间:3/3/2022 访问量:68853
我不明白 Python 中带有负边界的切片。这应该如何工作?[复制]
I don't understand slicing with negative bounds in Python. How is this supposed to work? [duplicate]
问:
我是 Python 的新手,在我的书中遇到了以下示例,该示例没有得到很好的解释。这是我从口译员那里打印出来的:
>>> s = 'spam'
>>> s[:-1]
'spa'
为什么没有开始绑定的切片和 a 会返回除最后一个元素之外的所有元素?调用在逻辑上和调用是一样的吗?它们都返回相同的结果。但我不确定 python 到底在做什么。任何帮助将不胜感激。'-1'
s[0:-1]
s[:-1]
答:
是的,打电话和打电话完全一样。s[0:-1]
s[:-1]
在 python 中使用负数作为索引会从列表的右侧返回第 n 个元素(而不是通常的左侧)。
因此,如果您有一个这样的列表:
myList = ['a', 'b', 'c', 'd', 'e']
print myList[-1] # prints 'e'
打印语句将打印“e”。
一旦你理解了这一点(你可能已经理解了,还不完全清楚这是否是你感到困惑的事情之一),我们就可以开始谈论切片了。
我将假设您了解切片的基础知识,并直接跳转到切片符号,其中一侧留空。myList[2:4]
['c', 'd']
正如您在帖子中怀疑的那样,与 .myList[:index]
myList[0:index]
顺便说一句,这也反过来工作...... 与 并将返回列表中所有元素的列表,从开始到结束(例如 将打印)。myList[index:]
myList[index:len(myList)]
index
print myList[2:]
['c', 'd', 'e']
作为第三个注意事项,您甚至可以在没有指示索引的地方执行此操作,这基本上将返回整个列表的副本(相当于 返回 ['a', 'b', 'c', 'd', 'e'])。如果您认为 myList 将在某个时候更改,但您希望将其副本保留在当前状态,这可能很有用。print myList[:]
myList[0:len(myList)]
如果你还没有这样做,我发现在 Python 解释器中搞砸一大堆对理解这些事情有很大帮助。我推荐 IPython。
评论
是的,调用在逻辑上与切片最好定义为:s[0:-1]
s[:-1]
[beginning_index:ending_index]
Python 允许你省略 0,因为这允许你的代码更简洁。
负指数从末尾开始计算,因此 s[:-1] 等价于 s[:len(s)-1],s[-1] 是最后一个元素。
关键的一点是,python 索引应该被视为指向列表中条目之间空格的指针,而不是指向元素本身的指针。因此,0 指向开头,1 指向第一个和第二个之间,...和 n 介于 n 和 (n+1)st 之间。
因此,l[1:2] 为您提供了一个仅包含元素 l[1] 的列表,因为它为您提供了两个指针之间的所有内容。
同样,负指数指向元素之间,但这次从后面开始计数,因此最后一个元素和倒数第二个元素之间有 -1 个点,因此 [0:-1] 指的是不包括最后一个元素的项目块。
作为句法糖,你可以从开头或实际上去掉 0,或者实际上,从结尾去掉 0,所以 l[n:] 指的是从 l[n] 到结尾的所有内容(如果 n>=len(l) 那么它返回空列表)。
评论
s[-2:]
s[-2:100]
>>> l = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz&']
# I want a string up to 'def' from 'vwx', all in between
# from 'vwx' so -2;to 'def' just before 'abc' so -9; backwards all so -1.
>>> l[-2:-9:-1]
['vwx', 'stu', 'pqr', 'mno', 'jkl', 'ghi', 'def']
# For the same 'vwx' 7 to 'def' just before 'abc' 0, backwards all -1
>>> l[7:0:-1]
['vwx', 'stu', 'pqr', 'mno', 'jkl', 'ghi', 'def']
请不要无精打采地列出。
- 首先编写第一个元素。您可以使用正指数或负指数 为此。我很懒惰,所以我使用正数,少一杆(低于 7 杆,或开始 -3)。
- 元素的索引,紧挨着要停止的位置。 同样,您可以使用正指数或负指数(低于 2 或 -8 表示止损)。
这里的标志很重要;当然 - 为了倒退;步幅的价值,你知道的。 步幅是一个具有大小和方向的“向量”(低于 -1,全部向后)。
l = [0,1,2,3,4,5,6,7,8,9] l[7:2:-1], l[-3:2:-1], [-3:-8:-1],l[7:-8:-1]
所有结果均为 .
[7, 6, 5, 4, 3]
如果我们想从字符串的后端打印,我们可以进行负索引。索引从 -1 开始。
示例 : s = 'hello world'
s[-11:-1] = '你好,你好' s[-1:-11] = '' // 起始值应该更低(即,在这种情况下,-1 大于 -11),如果它更大,则不会打印任何内容。
评论
我将解决其他一些人遗漏的一点:
根据我们对切片的了解,我们如何解释这个负指数?
一般来说,当我们做一个切片时,我们谈论的是[包容性,排他性]边界。所以
A = [1,3,4,6]
A[1:3] # will give us 3-1 = 2 elements, index 1 and 2 => [3,4]
因此,当我们在切片中有一个负索引时,这意味着我们有 =,这再次为我们提供了索引 1 和 2,因此 。A[1:-1]
A[1:len(A)-1]
A[1:3]
[3,4]
请注意,即使列表的长度为 4,其最后一个索引也是 3,因此这个 -1 表示法在这里有效。另请注意,如果您的代码将负索引作为变量,则需要手动检查 0,因为A[:-0] == A[:0] == []
`
RULE 或[START, END, STEP]
[START, END]
如果你想要一个有规律的切片,应该是积极的。
STEP
如果您想要一个相反顺序的切片,则应为负数。
STEP
例如:
>>> s = 'abcdefg'
>>> s[1:6:1]
'bcdef'
>>> s[6:1:1]
''
# If in regular order, s[6:1]='' therefore same for s[6:1:1]
>>> s[6:1:-1]
'gfedc'
# Reverse order, START=6 and END=1, a slice is possible
Python 在切片列表时到底在做什么,编码在 Python 源代码文件中。如果您没有遇到麻烦,无法清楚地看到以下代码行都没有给出异常或错误,或者您对切片的结果并不感到惊讶:sliceobject.c
assert [0,1,2,3][-23: 32] == [0,1,2,3]
assert [0,1,2,3][ 23: 32] == []
assert [0,1,2,3][-32:-23] == []
assert [0,1,2,3][ 23:-23:-1] == [3,2,1,0]
# ---
assert [0,1,2,3][ -1: 3:-1] == []
assert [0,1,2,3][ -1: 2:-1] == [3]
assert [0,1,2,3][ -1: 1:-1] == [3,2]
assert [0,1,2,3][ -1: 0:-1] == [3,2,1]
assert [0,1,2,3][ -1:-1:-1] == []
assert [0,1,2,3][ -1:-2:-1] == [3]
assert [0,1,2,3][ -1:-3:-1] == [3,2]
assert [0,1,2,3][ -1:-4:-1] == [3,2,1]
assert [0,1,2,3][ -1:-5:-1] == [3,2,1,0]
# ---
assert [0,1,2,3][321:-123: 1] == []
assert [0,1,2,3][321:-123:-1] == [3,2,1,0]
assert [0,1,2,3][-123:321:-1] == []
# ---
assert [0,1][None:None][None:][::][:] == [0,1]
您很有可能已经很好地了解了 Python 切片的工作原理。
如果您对一些给定的示例感到惊讶,查看 Python 源代码可能有助于解决混淆。我重写了返回开始、停止、步长值的 C 函数,然后可以使用这些值创建具有以下内容的切片:range()
listL
[ listL[indx] for indx in range(start,stop, step) ]
在 Python 中,并在下面提供。可以对其进行详细研究,以深入了解对列表进行切片背后的机制。我希望它能帮助您在切片列表时乍一看了解可能令人惊讶或难以掌握的负步骤和索引的结果。
顺便说一句:如果您想知道如何在使用否定步骤时到达列表的第一个元素,这里有一些选项可以做到这一点:
listL = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert listL[3: 2: -1] == [3]
assert listL[3: 1: -1] == [3, 2]
assert listL[3: 0: -1] == [3, 2, 1]
...
assert listL[3: -11: -1] == [3, 2, 1, 0]
assert listL[3:None: -1] == [3, 2, 1, 0]
assert listL[3::-1] == [3, 2, 1, 0]
assert listL[3:-321: -1] == [3, 2, 1, 0]
在 Python 脚本代码下方,相当于 sliceobject.c 文件中处理切片的 C 代码的一部分。请注意,[::] 中的缺失值将转换为 None,用于评估切片索引。我在代码中提供了一些注释,以帮助掌握什么和为什么:
# Python-3.9.10_srcCode/Objects/sliceobject.c:354:
# evaluate_slice_index(PyObject *v)
def evaluate_slice_index(v):
if v is None:
return None
if type(v) is int:
return v
if '__index__' in dir(v):
return v.__index__()
else:
raise TypeError(
"slice indices must be integers or None or have an __index__ method")
#:def
# Python-3.9.10_srcCode/Objects/sliceobject.c:372:
# _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
def _PySlice_GetLongIndices(objSlice, intLength):
'''
Compute slice indices given a slice and length.
Assumes that intLength is a nonnegative integer
'''
start=None; stop=None; step=None
upper=None; lower=None
# Convert step to an integer; raise for zero step.
if (objSlice.step is None):
step = 1
step_is_negative = False
else:
step = evaluate_slice_index(objSlice.step)
if ( step == 0 ):
raise ValueError( "slice step cannot be zero" )
if step < 0:
step_is_negative = True
else:
step_is_negative = False
# Find lower and upper bounds for start and stop.
if (step_is_negative):
lower = -1
upper = intLength + lower
else:
lower = 0
upper = intLength
# ^-- this is the 'trick' to cope with the stop value for range()
# providing values not including the stop value.
# Compute start:
if (objSlice.start == None):
start = upper if step_is_negative else lower
else:
start = evaluate_slice_index(objSlice.start)
if ( start < 0):
start = start + intLength
if start < lower:
start = lower
# ^-- explains how it comes, that any values
# for slice indices are OK.
else:
if start > upper:
start = upper
# ^-- explains how it comes, that any values
# for slice indices are OK.
# ^-- this is the 'trick' to get start from deliberate value
# into the range within the scope of valid list indices.
# The positive/negative step value case is already handled
# by the choice of values for lower and upper.
# Compute stop:
if (objSlice.stop == None):
stop = lower if step_is_negative else upper
else:
stop = evaluate_slice_index(objSlice.stop);
if ( stop < 0):
stop = stop + intLength
if (stop < lower):
stop = lower;
else:
if (stop > upper):
stop = upper;
# ^-- this is the 'trick' to get stop from deliberate value
# into the range within the scope of valid stop indices.
# The positive/negative step value case is already handled
# by the choice of values for lower and upper.
return (start, stop, step) # for range(start,stop,step) which can
# be used to obtain the slice from a list using:
# [ theList[index] for index in range(start, stop, step ]
#:def
# Let's check if the code above does the same as the .indices() function
# of the slice object:
listL = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for start in list(range(-3*len(listL), 3*len(listL))) + [None]:
for stop in list(range(-3*len(listL), 3*len(listL))) + [None]:
for step in list(range(-2*len(listL), 2*len(listL))) + [None]:
objSlice = slice(start,stop,step)
try:
py = objSlice.indices(intLength)
except:
try:
Py = _PySlice_GetLongIndices(objSlice, intLength)
echo(" STOP: difference in Exceptions")
import sys; sys.exit()
except:
continue
Py = _PySlice_GetLongIndices(objSlice, intLength)
assert py == Py
listL = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
intLength = len(listL)
# If you ever wondered how to get the first element of a list with
# a negative step, here two options how it can be done:
assert listL[3: 2:-1] == [3]
assert listL[3: 1:-1] == [3, 2]
assert listL[3: 0:-1] == [3, 2, 1]
...
assert listL[3: -11:-1] == [3, 2, 1, 0]
assert listL[3:None:-1] == [3, 2, 1, 0]
assert listL[3::-1] == [3, 2, 1, 0]
assert listL[3:-321:-1] == [3, 2, 1, 0]
# Both [:] and [::] have the same effect:
assert listL[:] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert listL[::] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
objSlice = slice(None,None,None) # equivalent to [::] and [:]
assert listL[objSlice] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# also all the over variants lead to same result:
assert listL[None:] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert listL[None:None] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert listL[None:None:None] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert [ listL[indx] for indx in range(2,1,1) ] == []
# ^-- Another 'trick' of slicing is to start with an empty list.
# If range(start,stop,step) don't deliver any value, an empty
# list without any added list elements is returned as result of
# slicing. Exceptions are raised only on zero step value and
# inappropriate types for start or stop values of the slice.
评论
sliceobject.c