Python的切片表示法是如何工作的?也就是说:当我编写[x:y:z]、a[:]、a]::2]等代码时,我如何理解哪些元素最终会出现在切片中?请在适当的地方附上参考资料。


另请参见:为什么切片和范围上限是互斥的?


当前回答

1.切片符号

为了简单起见,请记住切片只有一种形式:

s[start:end:step]

下面是它的工作原理:

s: 可以切片的物体start:开始迭代的第一个索引end:最后一个索引,请注意,最终的切片中不会包含结束索引step:每个步骤索引拾取元素

另一个重要的事情:所有开始、结束和步骤都可以省略!如果省略它们,则将使用它们的默认值:0,len(s),1。

因此,可能的变化如下:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

注意:如果start>=end(仅在步骤>0时考虑),Python将返回一个空切片[]。

2.陷阱

上面的部分解释了切片如何工作的核心特性,它将在大多数情况下工作。然而,可能会有陷阱,您应该注意,本部分将对它们进行解释。

负面指数

让Python学习者困惑的第一件事是索引可以是负数!不要惊慌:负指数意味着倒数。

例如:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

负阶跃

让事情更令人困惑的是,这一步也可能是消极的!

负步骤意味着向后迭代数组:从结束到开始,包括结束索引,从结果中排除开始索引。

注意:当step为负值时,start的默认值为len(s)(而end不等于0,因为s[::-1]包含s[0])。例如:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

超出范围错误?

请注意:当索引超出范围时,切片不会引发IndexError!

如果索引超出范围,Python将根据情况尽量将索引设置为0或len。例如:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3.示例

让我们用例子来完成这个回答,解释我们讨论的所有内容:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range

其他回答

解释Python的切片表示法

简而言之,下标表示法(subscriptable[subscriptarg])中的冒号(:)构成切片表示法,它具有可选的参数start、stop和step:

sliceable[start:stop:step]

Python切片是一种快速计算的方法,可以系统地访问部分数据。在我看来,要成为一名中级Python程序员,这是语言的一个必须熟悉的方面。

重要定义

首先,让我们定义几个术语:

start:切片的起始索引,除非它与stop相同,否则它将包含该索引处的元素,默认为0,即第一个索引。如果是负数,则表示从末尾开始n个项目。stop:切片的结束索引,它不包括该索引处的元素,默认为切片序列的长度,即,直到并包括结束。step:索引增加的量,默认为1。如果它是负的,那么你正在反向切片可迭代的。

索引的工作原理

你可以做这些正数或负数中的任何一个。正数的含义很简单,但对于负数,就像Python中的索引一样,从开始和停止的末尾开始向后计数,对于步骤,只需减少索引。此示例来自文档的教程,但我对其进行了轻微修改,以指示每个索引引用的序列中的哪个项:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

切片的工作原理

要对支持它的序列使用切片表示法,必须在序列后面的方括号中至少包含一个冒号(根据Python数据模型,这实际上实现了序列的__getitem_方法)

切片表示法的工作原理如下:

sequence[start:stop:step]

回想一下,start、stop和step都有默认值,所以要访问默认值,只需省略参数即可。

从列表(或任何其他支持它的序列,如字符串)中获取最后九个元素的切片表示法如下所示:

my_list[-9:]

当我看到这一点时,我把括号里的部分读成了“从结尾到结尾的第9个”(实际上,我在心里把它缩写为“-9,on”)

说明:

完整符号为

my_list[-9:None:None]

并替换默认值(实际上,当step为负值时,stop的默认值为-len(my_list)-1,因此None for stop实际上意味着它将转到任何结束步骤):

my_list[-9:len(my_list):1]

冒号:是告诉Python你给它一个切片,而不是一个常规索引。这就是为什么在Python2中制作列表浅拷贝的惯用方法是

list_copy = sequence[:]

清除它们的方法是:

del my_list[:]

(Python 3获得list.copy和list.clear方法。)

当步骤为负时,启动和停止的默认值将更改

默认情况下,当step参数为空(或None)时,将其赋值为+1。

但是您可以传入一个负整数,列表(或大多数其他标准可切片)将从结尾到开头进行切片。

因此,负切片将更改开始和停止的默认值!

在源中确认

我希望鼓励用户阅读源代码和文档。切片对象和此逻辑的源代码位于此处。首先,我们确定步骤是否为负:

step_is_negative=step_sign<0;

如果是这样的话,下限是-1,意味着我们一直切到并包括开头,上限是长度减1,意味着从结尾开始。(注意,这个-1的语义不同于用户可以在Python中传递指示最后一项的索引的-1。)

if(step_is_negative){lower=PyLong_FromLong(-1L);if(下限==NULL)转到错误;上限=PyNumber_Add(长度,下限);if(上限==NULL)转到错误;}

否则,step为正值,下限将为零,上限(我们将向上,但不包括)为切片列表的长度。

其他{lower=_PyLong_Zero;Py_INCREF(下部);上限=长度;Py_INCREF(上部);}

然后,我们可能需要应用start和stop的默认值。如果step为负值,那么start的默认值将计算为上限:

如果(self->start==Py_None){start=step_is_negative?上部:下部;Py_INCREF(启动);}

并停止,下限:

如果(self->stop==Py_None){stop=step_is_negative?下:上;Py_INCREF(停止);}

给你的切片起个描述性的名字!

您可能会发现将形成切片与将其传递到列表分开是很有用的__getitem_方法(这就是方括号的作用)。即使你不是新手,它也能让你的代码更可读,这样其他可能需要阅读你的代码的人就能更容易地理解你在做什么。

但是,不能只将一些用冒号分隔的整数分配给变量。您需要使用切片对象:

last_nine_slice = slice(-9, None)

第二个参数None是必需的,因此第一个参数被解释为开始参数,否则它将是停止参数。

然后可以将切片对象传递给序列:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

有趣的是,范围也可以分片:

>>> range(100)[last_nine_slice]
range(91, 100)

内存注意事项:

由于Python列表的切片会在内存中创建新的对象,因此需要注意的另一个重要函数是itertool.islice。通常,您需要对切片进行迭代,而不仅仅是在内存中静态创建。islice非常适合这个。需要注意的是,它不支持开始、停止或步骤的负参数,因此如果这是一个问题,您可能需要提前计算索引或反转可迭代项。

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

现在:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

列表切片复制是列表本身的一个特点。如果您正在切片像PandasDataFrame这样的高级对象,它可能会返回原始对象的视图,而不是副本。

在找到这张很棒的桌子http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

1.切片符号

为了简单起见,请记住切片只有一种形式:

s[start:end:step]

下面是它的工作原理:

s: 可以切片的物体start:开始迭代的第一个索引end:最后一个索引,请注意,最终的切片中不会包含结束索引step:每个步骤索引拾取元素

另一个重要的事情:所有开始、结束和步骤都可以省略!如果省略它们,则将使用它们的默认值:0,len(s),1。

因此,可能的变化如下:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

注意:如果start>=end(仅在步骤>0时考虑),Python将返回一个空切片[]。

2.陷阱

上面的部分解释了切片如何工作的核心特性,它将在大多数情况下工作。然而,可能会有陷阱,您应该注意,本部分将对它们进行解释。

负面指数

让Python学习者困惑的第一件事是索引可以是负数!不要惊慌:负指数意味着倒数。

例如:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

负阶跃

让事情更令人困惑的是,这一步也可能是消极的!

负步骤意味着向后迭代数组:从结束到开始,包括结束索引,从结果中排除开始索引。

注意:当step为负值时,start的默认值为len(s)(而end不等于0,因为s[::-1]包含s[0])。例如:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

超出范围错误?

请注意:当索引超出范围时,切片不会引发IndexError!

如果索引超出范围,Python将根据情况尽量将索引设置为0或len。例如:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3.示例

让我们用例子来完成这个回答,解释我们讨论的所有内容:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range

语法为:

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()对象的显式使用简化了切片的编程生成。

#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

你可以运行这个脚本并进行实验,下面是我从脚本中获得的一些示例。

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

当使用否定步骤时,请注意答案向右移动1。