我不明白 Python 中带有负边界的切片。这应该如何工作?[复制]

I don't understand slicing with negative bounds in Python. How is this supposed to work? [duplicate]

提问人:jtbradle 提问时间:1/30/2009 更新时间:3/3/2022 访问量:68853

问:

我是 Python 的新手,在我的书中遇到了以下示例,该示例没有得到很好的解释。这是我从口译员那里打印出来的:

>>> s = 'spam'
>>> s[:-1]
'spa'

为什么没有开始绑定的切片和 a 会返回除最后一个元素之外的所有元素?调用在逻辑上和调用是一样的吗?它们都返回相同的结果。但我不确定 python 到底在做什么。任何帮助将不胜感激。'-1's[0:-1]s[:-1]

Python 切片

评论

1赞 Claudio 3/3/2022
我在这里提供了有关 Python 在切片时究竟在做什么的详细信息:stackoverflow.com/a/71330285/7711283(包括 Python C 源代码部分的 Python 脚本代码,并添加了解释)。sliceobject.c

答:

45赞 Redwood 1/30/2009 #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)]indexprint myList[2:]['c', 'd', 'e']

作为第三个注意事项,您甚至可以在没有指示索引的地方执行此操作,这基本上将返回整个列表的副本(相当于 返回 ['a', 'b', 'c', 'd', 'e'])。如果您认为 myList 将在某个时候更改,但您希望将其副本保留在当前状态,这可能很有用。print myList[:]myList[0:len(myList)]

如果你还没有这样做,我发现在 Python 解释器中搞砸一大堆对理解这些事情有很大帮助。我推荐 IPython

评论

0赞 jtbradle 1/30/2009
谢谢!这解释了这一切。
0赞 Luke Woodward 1/30/2009
@Lawrence:在代码块之后的第 2 段中,mylist[2, 4] 应为 myList[2:4]。(对不起,我没有足够的代表进行编辑。
0赞 Redwood 1/30/2009
@Pourquoi Litytestdata:对错别字的好调用,现在已经修复。
1赞 Sun Shine 2/24/2020
@LawrenceJohnston,假设我有一个字符串 a='0123456789' 使用 a[-1:-4] 返回空?你能帮忙解释一下吗?它不应该至少打印 -1 索引,因为它是包容性的吗?
4赞 Andrew Hare 1/30/2009 #2

是的,调用在逻辑上与切片最好定义为:s[0:-1]s[:-1]

[beginning_index:ending_index]

Python 允许你省略 0,因为这允许你的代码更简洁。

6赞 MovEaxEsp 1/30/2009 #3

负指数从末尾开始计算,因此 s[:-1] 等价于 s[:len(s)-1],s[-1] 是最后一个元素。

8赞 Andrew Jaffe 1/30/2009 #4

关键的一点是,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) 那么它返回空列表)。

评论

0赞 Abgan 1/31/2009
“因此 l[1:2] 只给你元素 l[1]” - l[1:2] 返回一个只包含元素 l[1] 的列表。这与元素 l[1] 不同。您可以对其进行编辑,以免使读者感到困惑。
0赞 Abgan 1/31/2009
l[n:] 指的是一个空列表——一个“在最后一个元素后面”的列表。这个也可以纠正:-)
0赞 Andrew Jaffe 1/31/2009
(实际上,对于 l[n:] 我并不是说 n=len(l),但你是对的,不清楚......两者都修复了。
0赞 Niels 6/22/2022
这个答案让我建立了直觉,知道它为什么或以这种方式工作。s[-2:]s[-2:100]
17赞 Rathinavelu Muthaliar 1/4/2017 #5
>>> 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']

请不要无精打采地列出。

  1. 首先编写第一个元素。您可以使用正指数或负指数 为此。我很懒惰,所以我使用正数,少一杆(低于 7 杆,或开始 -3)。
  2. 元素的索引,紧挨着要停止的位置。 同样,您可以使用正指数或负指数(低于 2 或 -8 表示止损)。
  3. 这里的标志很重要;当然 - 为了倒退;步幅的价值,你知道的。 步幅是一个具有大小和方向的“向量”(低于 -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]

2赞 VIJAYA SRI 3/11/2019 #6

如果我们想从字符串的后端打印,我们可以进行负索引。索引从 -1 开始。

示例 : s = 'hello world'

s[-11:-1] = '你好,你好' s[-1:-11] = '' // 起始值应该更低(即,在这种情况下,-1 大于 -11),如果它更大,则不会打印任何内容。

评论

2赞 Andrei Berenda 3/11/2019
你应该这样做:s[-1:-11:-1],最后一个索引表示步骤
4赞 information_interchange 11/4/2019 #7

我将解决其他一些人遗漏的一点:

根据我们对切片的了解,我们如何解释这个负指数?

一般来说,当我们做一个切片时,我们谈论的是[包容性,排他性]边界。所以

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] == [] `

1赞 Lluvio Liu 2/9/2022 #8

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
2赞 Claudio 3/3/2022 #9

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.  

评论

0赞 Riddle00 9/4/2022
感谢您提供非常详细的答案并花时间编写 Python 代码。我正在寻找那个!