23

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

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

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

4

9 回答 9

41

s[0:-1]的,调用与调用完全相同s[:-1]

在 python 中使用负数作为索引返回列表右侧的第 n 个元素(与通常的左侧相反)。

所以如果你有一个这样的列表:

myList = ['a', 'b', 'c', 'd', 'e']
print myList[-1] # prints 'e'

print 语句将打印“e”。

一旦你理解了这一点(你可能已经明白了,不完全清楚这是否是你感到困惑的事情之一),我们就可以开始讨论切片了。

我将假设您了解切片的基础知识myList[2:4](将返回['c', 'd'])并直接跳入切片符号,其中一侧留空。

正如您在帖子中所怀疑的那样,myList[:index]myList[0:index].

顺便说一下,这也适用于其他方式...myList[index:]myList[index:len(myList)]并且将返回列表中从开始index到结束的所有元素的列表(例如print myList[2:]将 print ['c', 'd', 'e'])。

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

如果您还没有这样做,我会发现只是在 Python 解释器中乱搞对理解这些事情有很大帮助。我推荐IPython

于 2009-01-29T19:34:38.387 回答
11
>>> 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].

于 2017-01-03T18:28:25.720 回答
6

例如,负索引从末尾开始计算,因此 s[:-1] 等价于 s[:len(s)-1] 并且 s[-1] 是最后一个元素。

于 2009-01-29T19:40:49.497 回答
6

关键的一点是,python 索引应该被认为是指向列表中条目之间的空格的指针,而不是指向元素本身的指针。因此,0 指向开头,1 指向第一个和第二个之间,... 和 n 指向第 n 个和 (n+1)st 之间。

因此 l[1:2] 为您提供了一个仅包含元素 l[1] 的列表,因为它为您提供了两个指针之间的所有内容。

类似地,负索引指向元素之间,但这次从后面计数,所以 -1 指向最后一个元素和倒数第二个元素之间,因此 [0:-1] 指的是不包括最后一个元素的项目块一。

作为语法糖,您可以从开头省略 0,或者实际上是结尾,因此 l[n:] 指的是从 l[n] 到结尾的所有内容(如果 n>=len(l) 则返回空列表)。

于 2009-01-29T19:42:35.250 回答
4

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

[beginning_index:ending_index]

Python 允许您省略 0,因为这可以让您的代码更简洁。

于 2009-01-29T19:35:38.900 回答
2

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

示例:s = '你好世界'

s[-11:-1] = 'hello worl' s[-1:-11] = '' // 起始值应该更低(即,在这种情况下,-1 大于 -11)如果大于则获胜不打印任何东西。

于 2019-03-11T03:44:58.010 回答
2

我将解决其他一些人错过的一点:

在我们对切片的了解的背景下,我们如何解释这个负指数?

通常,当我们进行切片时,我们会谈论 [inclusive,exclusive] 边界。所以

A = [1,3,4,6]
A[1:3] # will give us 3-1 = 2 elements, index 1 and 2 => [3,4]

因此,当我们在切片中有一个负索引时A[1:-1],这意味着我们有A[1:len(A)-1]= A[1:3],这又给了我们索引 1 和 2,因此是[3,4]

请注意,即使列表的长度为 4,它的最后一个索引也是 3,因此这个 -1 表示法在这里可以工作。另请注意,如果您有将负索引作为变量的代码,那么您需要手动检查 0,因为 A[:-0] == A[:0] == [] `

于 2019-11-03T18:23:59.760 回答
0

规则 [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
于 2022-02-08T20:01:47.800 回答
0

Python 在对列表进行切片时到底在做什么是在sliceobject.cPython 源代码文件中编码的。如果您没有麻烦地清楚地看到下面的代码行都没有给出异常或错误,或者您对切片的结果并不感到惊讶:

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()然后可以使用这些值创建 a 的切片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.  
于 2022-03-02T23:12:43.140 回答