Python 中的切片工作原理

How slicing in Python works

提问人:Simon 提问时间:2/4/2009 最后编辑:Mateen UlhaqSimon 更新时间:11/22/2023 访问量:2982874

问:

Python 的切片表示法是如何工作的?也就是说:当我编写像 、 等代码时,我如何理解哪些元素最终出现在切片中?a[x:y:z]a[:]a[::2]


请参阅为什么切片和范围上限是独占的? 了解为什么 xs[0:2] == [xs[0], xs[1]],而不是 [..., xs[2]]
请参阅为 xs[::N] 创建包含原始列表中每个第 N 项的新列表
请参阅赋值如何使用列表切片? 了解 xs[0:2] = [“a”, “b”] 的作用。

Python 切片 序列

评论


答:

6454赞 Greg Hewgill 2/4/2009 #1

语法为:

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

还有值,可以与上述任何一项一起使用:step

a[start:stop:step] # start through not past stop, by step

要记住的关键点是,该值表示不在所选切片中的第一个值。因此,和之间的差值是所选元素的数量(如果为 1,则为默认值)。:stopstopstartstep

另一个特征是 or 可能是一个数,这意味着它从数组的末尾而不是开头开始计数。所以:startstop

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

同样,可以是负数:step

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 对程序员很友好。例如,如果您请求并且只包含一个元素,则会得到一个空列表而不是错误。有时您更喜欢错误,因此您必须意识到可能会发生这种情况。a[:-2]a

与对象的关系slice

切片对象可以表示切片操作,即:

a[start:stop:step]

相当于:

a[slice(start, stop, step)]

Slice 对象的行为也略有不同,具体取决于参数的数量,类似于 ,即 both 和 are supported。 要跳过指定给定的参数,可以使用 ,例如 等同于或等同于 。range()slice(stop)slice(start, stop[, step])Nonea[start:]a[slice(start, None)]a[::-1]a[slice(None, None, -1)]

虽然基于 -的表示法对于简单的切片非常有帮助,但显式使用对象简化了切片的编程生成。:slice()

评论

198赞 Beni Cherniavsky-Paskin 9/23/2013
对内置类型进行切片会返回一个副本,但这不是通用的。值得注意的是,对 NumPy 数组进行切片会返回一个与原始数组共享内存的视图。
147赞 Mark Ransom 1/17/2019
这是一个美丽的答案,有选票来证明这一点,但它遗漏了一件事:你可以替换任何空白。例如,制作一个完整的副本。当您需要使用变量指定范围的末尾并需要包含最后一项时,这很有用。None[None:None]
14赞 vreyespue 5/29/2019
请注意,与通常的 Python 切片(见上文)相反,在 Pandas Dataframe 中,当索引中存在时,开始和停止都包括在内。有关详细信息,请参阅 Pandas 索引文档
34赞 axell-brendow 6/30/2019
真正让我烦恼的是,python 说当你不设置开始和结束时,它们默认为 0 和序列长度。因此,从理论上讲,当您使用 “abcdef”[::-1] 时,它应该转换为 “abcdef”[0:6:-1],但这两个表达式不会得到相同的输出。我觉得自从语言创建以来,python文档中缺少一些东西。
41赞 axell-brendow 6/30/2019
我知道“abcdef”[::-1] 被转换为“abcdef”[6:-7:-1],所以,最好的解释方式是:让 len 是序列的长度。如果 step 为正数,则 start 和 end 的默认值为 0 和 len否则,如果 step 为负数,则 start 和 end 的默认值为 len 和 -len - 1。
698赞 Hans Nowak 2/4/2009 #2

Python 教程对此进行了讨论(向下滚动一点,直到进入有关切片的部分)。

ASCII 艺术图对于记住切片的工作原理也很有帮助:

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

记住切片工作原理的一种方法是将索引视为指向字符之间,第一个字符的左边缘编号为 0。然后,n 个字符字符串的最后一个字符的右边缘具有索引 n

评论

42赞 aguadopd 5/28/2019
这个建议适用于积极的步幅,但不适用于消极的步幅。从图中可以看出,我希望是这样,但事实并非如此。总是有效的是思考字符或插槽,并使用索引作为半开间隔 - 如果正步幅,则右开,如果负步幅,则左开。a[-4,-6,-1]yPty
0赞 endolith 7/7/2019
但是没有办法从头到尾折叠成一个空集(就像从头开始时那样),所以你必须特例小数组。:/x[:0]
4赞 Javier Ruiz 4/6/2021
@aguadopd 你是绝对正确的。解决方案是将索引向右移动,在字符正下方居中,并注意始终排除止损。请参阅下面的另一个回复。
1赞 aguadopd 4/15/2021
我评论的附录:请看我的回答,图表如下: stackoverflow.com/a/56332104/2343869
540赞 ephemient 2/4/2009 #3

列举序列的语法所允许的可能性:x

>>> x[:]                # [x[0],   x[1],          ..., x[-1]    ]
>>> x[low:]             # [x[low], x[low+1],      ..., x[-1]    ]
>>> x[:high]            # [x[0],   x[1],          ..., x[high-1]]
>>> x[low:high]         # [x[low], x[low+1],      ..., x[high-1]]
>>> x[::stride]         # [x[0],   x[stride],     ..., x[-1]    ]
>>> x[low::stride]      # [x[low], x[low+stride], ..., x[-1]    ]
>>> x[:high:stride]     # [x[0],   x[stride],     ..., x[high-1]]
>>> x[low:high:stride]  # [x[low], x[low+stride], ..., x[high-1]]

当然,如果 ,那么终点会比 低一点。(high-low)%stride != 0high-1

如果为负数,则由于我们正在倒计时,顺序会稍作更改:stride

>>> x[::-stride]        # [x[-1],   x[-1-stride],   ..., x[0]    ]
>>> x[high::-stride]    # [x[high], x[high-stride], ..., x[0]    ]
>>> x[:low:-stride]     # [x[-1],   x[-1-stride],   ..., x[low+1]]
>>> x[high:low:-stride] # [x[high], x[high-stride], ..., x[low+1]]

扩展切片(使用逗号和省略号)通常仅由特殊数据结构(如 NumPy)使用;基本序列不支持它们。

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

评论

0赞 liyuan 1/2/2018
实际上仍然遗漏了一些东西,例如,如果我输入“apple”[4:-4:-1],我会得到“elp”,python 正在将 -4 转换为 1 也许?
0赞 wjandrea 1/27/2019
请注意,反引号已被弃用,取而代之的是repr
0赞 chepner 9/10/2019
@liyuan 实现的类型是;您的示例等效于 。__getitem__apple[slice(4, -4, -1)]
0赞 Bananeen 12/20/2021
前两张桌子是纯金的。
161赞 Dana 2/4/2009 #4

当我第一次看到切片语法时,有几件事对我来说并不明显:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

反转序列的简单方法!

如果您出于某种原因想要相反顺序中的每一项:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]
45赞 Steve Losh 2/7/2009 #5

我自己使用“元素之间的索引点”方法来思考它,但有时可以帮助其他人理解它的一种描述方式是这样的:

mylist[X:Y]

X 是所需第一个元素的索引。
Y 是不需要的第一个元素的索引。

评论

0赞 contactmatt 1/31/2023
这很有帮助;我曾思考过为什么不包括结束索引(在本例中为 [X:Y] 的 Y)。即为什么 [0:0] 不包括第一个索引。
70赞 Simon 2/20/2009 #6

使用了一会儿后,我意识到最简单的描述是它与循环中的参数完全相同......for

(from:to:step)

其中任何一个都是可选的:

(:to:step)
(from::step)
(from:to)

然后负索引只需要您将字符串的长度添加到负索引中即可理解它。

无论如何,这对我有用......

445赞 David M. Perlman 1/19/2011 #7

上面的答案不讨论切片分配。要理解切片分配,在 ASCII 艺术中添加另一个概念会很有帮助:

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

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

一种启发式方法是,对于从零到n的切片,可以考虑:“零是开始,从头开始,并在列表中获取n个项目”。

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

另一个启发式方法是,“对于任何切片,将开头替换为零,应用先前的启发式方法以获取列表的末尾,然后向上数第一个数字以从开头切掉项目”

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

切片赋值的第一条规则是,由于切片返回一个列表,因此切片赋值需要一个列表(或其他可迭代的):

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

切片分配的第二条规则(也可以在上面看到)是,切片索引返回列表的任何部分,都与切片分配更改的部分相同:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

切片分配的第三条规则是,分配的列表(可迭代)不必具有相同的长度;索引切片被简单地切掉,并被分配的任何内容集体替换:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

最棘手的部分是分配给空切片。使用启发式 1 和 2,可以很容易地为空切片编制索引

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

然后,一旦你看到了这一点,将切片分配给空切片也是有意义的:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

请注意,由于我们没有更改切片的第二个数字 (4),因此插入的项目始终与“o”堆叠在一起,即使我们分配给空切片也是如此。因此,空切片分配的位置是非空切片分配位置的逻辑扩展。

稍微回过头来,当你继续我们开始计算切片的游行时会发生什么?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

对于切片,一旦你完成了,你就完成了;它不会开始向后切片。在 Python 中,除非您使用负数明确要求它们,否则您不会获得负步幅。

>>> p[5:3:-1]
 ['n','o']

“一旦你完成了,你就完成了”规则有一些奇怪的后果:

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

事实上,与索引相比,Python 切片具有奇怪的防错性:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

这有时会派上用场,但也可能导致一些奇怪的行为:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

根据您的应用,这可能...或者可能不会......成为你所希望的!


以下是我的原始答案的文本。它对很多人都很有用,所以我不想删除它。

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

这也可以阐明切片和索引之间的区别。

评论

0赞 Alex O 7/25/2022
如果我想删除列表中的第一个 x 元素,哪个会更好:或者?l = l[6:]l[:] = l[6:]
0赞 David M. Perlman 7/26/2022
第一种方法适用于列表或字符串;第二种方法仅适用于列表,因为不允许对字符串进行切片分配。除此之外,我认为唯一的区别是速度:看起来第一种方式要快一点。自己尝试使用 timeit.timeit() 或最好是 timeit.repeat()。它们非常易于使用且非常有教育意义,值得习惯一直玩它们!
0赞 Edamame 9/2/2022
好奇做的时间复杂度是多少?谢谢!r[1:1]=['blah']
0赞 Simo 4/6/2023
p[2:3] = 't' 工作正常!不应该有 TypeError !
103赞 AdrianoFerrari 9/6/2011 #8

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)
34赞 Arindam Roychowdhury 3/23/2012 #9

这只是一些额外的信息...... 请考虑下面的列表

>>> l=[12,23,345,456,67,7,945,467]

反转列表的其他一些技巧:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
55赞 Beni Cherniavsky-Paskin 3/29/2012 #10

我发现更容易记住它是如何工作的,然后我可以弄清楚任何特定的开始/停止/步骤组合。

首先了解一下很有启发性:range()

def range(start=0, stop, step=1):  # Illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

从 开始 ,以 为增量,不要达到 。很简单。startstepstop

关于负步,要记住的是,无论它是高还是低,它始终是被排除在外的一端。如果你想以相反的顺序处理相同的切片,那么单独进行反转会干净得多:例如 从左边切掉一个字符,从右边切掉两个字符,然后反转。(参见 reversed()。stop'abcde'[1:-2][::-1]

序列切片是一样的,只是它首先对负索引进行归一化,并且它永远不能超出序列:

TODO:下面的代码有一个错误,当 abs(step)>1;我想我修补了它是正确的,但很难理解。

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

不要担心细节 - 只要记住省略和/或总是做正确的事情来给你整个序列。is Nonestartstop

首先对负索引进行归一化允许从末尾独立地计算开始和/或停止:尽管 . 归一化有时被认为是“对长度进行取模”,但请注意,它只增加一次长度:例如 只是整个字符串。'abcde'[1:-2] == 'abcde'[1:3] == 'bc'range(1,-2) == []'abcde'[-53:42]

评论

3赞 Eastsun 10/29/2016
与 python slice 不同。例如: 将在 Python 中得到 [0],但得到 [1]。this_is_how_slicing_works[0, 1, 2][-5:3:3]list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))
0赞 Beni Cherniavsky-Paskin 10/30/2016
@Eastsun 哎呀,你是对的!一个更清晰的例子:但是.我试图简单地实现“永远不要超出序列”,但对于步骤>1来说是错误的。我将在今天晚些时候重写它(通过测试)。range(4)[-200:200:3] == [0, 3]list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]if 0 <= i < len(seq):
112赞 Ankur Agarwal 10/22/2012 #11

在 Python 2.7 中

Python 中的切片

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

了解索引赋值非常重要。

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

当你说 [a:b:c] 时,你是在说取决于 c 的符号(向前或向后),从 a 开始,到 b 结束(不包括第 b 索引处的元素)。使用上面的索引规则,请记住,您只能在此范围内找到元素:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

但这个范围在两个方向上无限地继续:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

例如:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

如果选择的 a、b 和 c 允许在使用上述 a、b、c 规则遍历时与上述范围重叠,则将获得一个包含元素的列表(在遍历过程中触及),或者您将获得一个空列表。

最后一件事:如果 a 和 b 相等,那么你也会得到一个空列表:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]

评论

4赞 Deviacium 7/10/2017
另一个有趣的例子:结果是a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2][9]
43赞 xiaoyu 2/4/2013 #12
Index:
      ------------>
  0   1   2   3   4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
  0  -4  -3  -2  -1
      <------------

Slice:
    <---------------|
|--------------->
:   1   2   3   4   :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
:  -4  -3  -2  -1   :
|--------------->
    <---------------|

我希望这能帮助您在 Python 中对列表进行建模。

参考: http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

35赞 dansalmo 4/5/2013 #13

您还可以使用切片分配从列表中删除一个或多个元素:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
39赞 Brent Bradburn 4/29/2013 #14

Python 切片符号:

a[start:end:step]
  • 对于 和 ,负值被解释为相对于序列的末尾。startend
  • 正索引表示要包含的最后一个元素之后的位置。end
  • 默认值为空,如下所示:。[+0:-0:1]
  • 使用负步长会颠倒对 和 的解释startend

符号扩展到 (numpy) 矩阵和多维数组。例如,要对整个列进行切片,您可以使用:

m[::,0:2:] ## slice the first two columns

切片保存数组元素的引用,而不是副本。如果你想将一个单独的副本变成一个数组,你可以使用 deepcopy()。

29赞 Python_Dude 12/8/2013 #15

作为一般规则,使用大量硬编码索引值编写代码会导致可读性 和维护混乱。例如,如果您在一年后回到代码,您将 看着它,想知道你在写它时在想什么。所示的解决方案 只是一种更清楚地说明您的代码实际在做什么的方法。 通常,内置的 slice() 会创建一个 slice 对象,该对象可以在 slice 的任何位置使用 是允许的。例如:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

如果你有一个切片实例,你可以通过查看它的 s.start、s.stop 和 s.step 属性。例如:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>
293赞 Russia Must Remove Putin 7/12/2014 #16

解释 Python 的切片表示法

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

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]

回想一下,startstopstep 有默认值,因此要访问默认值,只需省略参数即可。

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

my_list[-9:]

当我看到这个时,我把括号中的部分读为“从头到尾的第 9 位”。(实际上,我在心理上将其缩写为“-9,on”)

解释:

完整的符号是

my_list[-9:None:None]

并替换默认值(实际上 when 为负数,默认值为 ,所以 for stop 实际上只是意味着它进入任何结束步骤):stepstop-len(my_list) - 1None

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

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

list_copy = sequence[:]

清除它们是:

del my_list[:]

(Python 3 获取 and 方法。list.copylist.clear

如果为负数,则 和 的默认值将更改stepstartstop

默认情况下,当参数为空(或 )时,将分配给 。stepNone+1

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

因此,负切片将更改 和 的默认值!startstop

在源代码中确认这一点

我喜欢鼓励用户阅读源代码和文档。切片对象和此逻辑的源代码可在此处找到。首先,我们确定是否为负数:step

step_is_negative = step_sign < 0;

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

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

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

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

然后,我们可能需要应用默认值 for 和 - 默认值 then for 计算为负数时的上限:startstopstartstep

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

和 ,下限:stop

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

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

您可能会发现将形成切片与将其传递给方法分开很有用(这就是方括号的作用)。即使你不熟悉它,它也能使你的代码更具可读性,以便其他可能必须阅读你的代码的人可以更容易地理解你在做什么。list.__getitem__

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

last_nine_slice = slice(-9, None)

第二个参数 是必需的,因此第一个参数被解释为参数,否则它将是 stop 参数Nonestart

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

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

有趣的是,范围也需要切片:

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

内存注意事项:

由于 Python 列表的切片会在内存中创建新对象,因此需要注意的另一个重要功能是 。通常,您需要迭代切片,而不仅仅是在内存中静态创建切片。 非常适合此。需要注意的是,它不支持 、 或 的否定参数,因此如果这是一个问题,您可能需要提前计算索引或反转可迭代对象。itertools.isliceislicestartstopstep

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]

列表切片制作副本的事实是列表本身的一个功能。如果要对高级对象(如 Pandas DataFrame)进行切片,它可能会返回原始视图,而不是副本。

评论

0赞 Russia Must Remove Putin 9/30/2020
@WinEunuuchs2Unix这是一个很好的反馈 - 这是一个标准的 Python 行为,但可以通过这种方式更清晰,所以我会考虑更新我的材料以包含这个语义。
0赞 Max 11/30/2022
当你写“可切片”时,你的答案是唯一一个 (?) 触及这里有趣的提示 - 剩下的就是琐碎了。我想知道使用这种方法是如何切片的。但是,如果我理解得很好,你必须自己完成所有这些工作:检查你的 arg 是 int 还是 slice(或者它还能是什么?),在那种 (slice) 情况下,自己处理所有可能的情况((A) 或 (A,B) 或 (A,B,C),以及所有可能的符号组合)。是吗?__getitem____getitem__
17赞 mahmoh 10/19/2014 #17
#!/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。

40赞 Chillar Anand 3/25/2015 #18

这就是我向新手教授切片的方式:

了解索引和切片之间的区别:

Wiki Python 有这张惊人的图片,可以清楚地区分索引和切片。

Enter image description here

它是一个包含六个元素的列表。为了更好地理解切片,请将该列表视为一组六个框。每个盒子里都有一个字母表。

索引就像处理盒子的内容一样。您可以检查任何框的内容。但是您无法一次检查多个框的内容。您甚至可以更换盒子里的东西。但是你不能把两个球放在一个盒子里,或者一次更换两个球。

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_GetIndicesEx() 的函数,它计算出任何给定参数的切片索引。这是 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 Slices 背后的智能中。

评论

1赞 Olivier 1/30/2022
最后,我在这里找到了一些关于为什么切片参数和防错的解释。startstop
14赞 Robert 5/26/2016 #19

我的大脑似乎很乐意接受包含第 -s 项的内容。我甚至可以说这是一个“自然假设”。lst[start:end]start

但偶尔会出现一个疑问,我的大脑要求保证它不包含第 -th 个元素。end

在这些时刻,我依赖于这个简单的定理:

for any n,    lst = lst[:n] + lst[n:]

这个漂亮的属性告诉我它不包含第 -th 项,因为它位于 .lst[start:end]endlst[end:]

请注意,这个定理对任何人都是正确的。例如,您可以检查n

lst = range(10)
lst[:-42] + lst[-42:] == lst

返回。True

31赞 cizixs 1/9/2017 #20

1. 切片表示法

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

s[start:end:step]

这是它的工作原理:

  • s:可以切片的对象
  • start:第一个开始迭代的索引
  • end:最后一个索引,请注意,结束索引将不会包含在结果切片中
  • step:选择每个索引的元素step

另一个导入的东西:所有的开始结束步骤都可以省略!如果省略它们,则将相应地使用它们的默认值:,。0len(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[:]

注意:如果(仅考虑当 ),Python 将返回一个空切片。start >= endstep>0[]

2. 陷阱

上面的部分解释了 slice 工作原理的核心功能,它在大多数情况下都可以工作。但是,您可能应该注意一些陷阱,本部分将对此进行解释。

负指数

让 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].

负步长

更令人困惑的是,这一步也可能是负面的!

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

注意:当步长为负数时,默认值为 (while 不等于 ,因为包含 )。例如:startlen(s)end0s[::-1]s[0]

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

超出范围错误?

令人惊讶的是:当索引超出范围时,slice 不会引发 IndexError!

如果索引超出范围,Python 会尽量根据情况设置索引。例如:0len(s)

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
29赞 Statham 3/1/2017 #21

前面的答案没有讨论使用著名的 NumPy 包可以实现的多维数组切片:

切片也可以应用于多维数组。

# Here, a is a NumPy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
       [5, 7]])

逗号前的“”在一维上操作,逗号后的“”在二维上操作。:20:3:2

评论

4赞 Mars Lee 7/27/2019
友情提醒您,您不能在 Python 上执行此操作,而只能在 Numpy 中执行此操作listarray
9赞 Prince Dhadwal 7/28/2017 #22

下面是字符串索引的示例:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

切片示例:[start:end:step]

str[start:end] # Items start through end-1
str[start:]    # Items start through the rest of the array
str[:end]      # Items from the beginning through end-1
str[:]         # A copy of the whole array

以下是用法示例:

print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti
13赞 lmiguelvargasf 9/5/2017 #23

在 Python 中,切片的最基本形式如下:

l[start:end]

其中是一些集合,是包容性索引,并且是独占索引。lstartend

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

从头开始切片时,可以省略零索引,切片到最后时,可以省略最终索引,因为它是多余的,所以不要冗长:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

负整数在相对于集合末尾进行偏移时很有用:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

可以在切片时提供越界的索引,例如:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

请记住,对集合进行切片的结果是一个全新的集合。此外,在分配中使用切片表示法时,切片分配的长度不必相同。将保留分配的切片之前和之后的值,并且集合将收缩或增长以包含新值:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

如果省略开始和结束索引,则将创建集合的副本:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

如果在执行赋值操作时省略了开始索引和结束索引,则集合的全部内容将替换为所引用内容的副本:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

除了基本切片外,还可以应用以下符号:

l[start:end:step]

其中 是一个集合,是一个包含索引,是一个独占索引,并且是一个步幅,可用于获取 中的每一lstartendstepl

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

Using 提供了一个有用的技巧来反转 Python 中的集合:step

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

也可以使用负整数,如以下示例所示:step

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

但是,使用负值可能会变得非常混乱。此外,为了成为 Pythonic,您应该避免在单个切片中使用 、 和。如果需要这样做,请考虑在两个作业中执行此操作(一个用于切片,另一个用于跨步)。stepstartendstep

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]
11赞 Roshan Bagdiya 10/7/2017 #24

前面的大多数答案都解决了有关切片表示法的问题。

用于切片的扩展索引语法是 ,基本示例如下:aList[start:stop:step]

Enter image description here:

更多切片示例:15 个扩展切片

18赞 asiby 12/12/2017 #25

在我看来,如果您以下方式查看 Python 字符串切片符号,您将更好地理解和记忆它(继续阅读)。

让我们使用以下字符串...

azString = "abcdefghijklmnopqrstuvwxyz"

对于那些不知道的人,您可以使用符号创建任何子字符串azStringazString[x:y]

来自其他编程语言,这就是常识受到损害的时候。什么是 x 和 y?

我不得不坐下来运行几个场景,以寻求一种记忆技巧,以帮助我记住 x 和 y 是什么,并帮助我在第一次尝试时正确地切开琴弦。

我的结论是,x 和 y 应该被看作是我们想要添加的字符串周围的边界索引。因此,我们应该将表达式视为 ,甚至更清晰地称为 。azString[index1, index2]azString[index_of_first_character, index_after_the_last_character]

下面是一个可视化示例......

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

因此,您所要做的就是将 index1 和 index2 设置为将围绕所需子字符串的值。例如,要获取子字符串“cdefgh”,可以使用 ,因为 “c” 左侧的索引是 2,“h” 右侧大小的索引是 8。azString[2:8]

请记住,我们正在设定界限。这些边界是您可以放置一些括号的位置,这些括号将像这样缠绕在子字符串上......

a b [ c d e f g h ] i j

这个技巧一直有效,很容易记住。

8赞 Shital Shah 12/19/2017 #26

如果您觉得切片中的负索引令人困惑,这里有一个非常简单的方法来考虑它:只需将负索引替换为 .例如,将 -3 替换为 .len - indexlen(list) - 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)]
12赞 Jeyekomon 4/4/2018 #27

我想添加一个 Hello, World! 示例,为初学者解释切片的基础知识。这对我帮助很大。

让我们有一个包含六个值的列表:['P', 'Y', 'T', 'H', 'O', 'N']

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5

现在,该列表最简单的切片是其子列表。符号是,关键是要这样读:[<index>:<index>]

[ start cutting before this index : end cutting before this index ]

现在,如果您从上面的列表中切片,就会发生这种情况:[2:5]

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5

您在带索引的元素之前进行了一次剪切,在带索引元素之前进行了另一个剪切。因此,结果将是这两个切口之间的切片,一个列表。25['T', 'H', 'O']

7赞 Babu Chandermani 7/23/2018 #28

基本的切片技术是定义起点、终点和步长,也称为步幅。

首先,我们将创建一个用于切片的值列表。

创建两个列表进行切片。第一个是从 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])

基本切片

用于切片的扩展索引语法是 。start 参数和 step 参数都默认为 — 唯一必需的参数是 stop。您是否注意到这与使用范围定义列表 A 和 B 的方式类似?这是因为 slice 对象表示 指定的索引集。aList[start:stop:step]Nonerange(start, stop, step)

如您所见,仅定义 stop 会返回一个元素。由于 start 默认为 none,这意味着仅检索一个元素。

需要注意的是,第一个元素是索引 0,而不是索引 1。这就是为什么我们在本练习中使用 2 个列表的原因。列表 A 的元素根据序号位置进行编号(第一个元素是 1,第二个元素是 2,依此类推),而列表 B 的元素是用于索引它们的数字(对于第一个元素,0 等)。[0]

使用扩展索引语法,我们检索一系列值。例如,所有值都使用冒号进行检索。

A[:]

要检索元素的子集,需要定义开始和停止位置。

给定模式,从列表 A 中检索前两个元素。aList[start:stop]

2赞 Banghua Zhao 12/28/2018 #29

如果我们可以将切片与给出索引的切片联系起来,就很容易理解了。我们可以将切片分为以下两类:range


1.没有步骤或步骤>0。例如,或 (k>0)[i:j][i:j:k]

假设序列为 。s=[1,2,3,4,5]

  • 如果 和 ,则0<i<len(s)0<j<len(s)[i:j:k] -> range(i,j,k)

例如[0:3:2] -> range(0,3,2) -> 0, 2

  • 如果 或 、 或i>len(s)j>len(s)i=len(s)j=len(s)

例如[0:100:2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4

  • 如果 或 、 或i<0j<0i=max(0,len(s)+i)j=max(0,len(s)+j)

例如[0:-3:2] -> range(0,len(s)-3,2) -> range(0,2,2) -> 0

再举一个例子,[0:-1:2] -> range(0,len(s)-1,2) -> range(0,4,2) -> 0, 2

  • 如果未指定,则ii=0

例如[:4:2] -> range(0,4,2) -> range(0,4,2) -> 0, 2

  • 如果未指定,则jj=len(s)

例如[0::2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4


2. 步骤 < 0.例如,(k<0)[i:j:k]

假设序列为 。s=[1,2,3,4,5]

  • 如果 和 ,则0<i<len(s)0<j<len(s)[i:j:k] -> range(i,j,k)

例如[5:0:-2] -> range(5,0,-2) -> 5, 3, 1

  • 如果 或 、 或i>len(s)j>len(s)i=len(s)-1j=len(s)-1

例如[100:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2

  • 如果 或 、 或i<0j<0i=max(-1,len(s)+i)j=max(-1,len(s)+j)

例如[-2:-10:-2] -> range(len(s)-2,-1,-2) -> range(3,-1,-2) -> 3, 1

  • 如果未指定,则ii=len(s)-1

例如[:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2

  • 如果未指定,则jj=-1

例如[2::-2] -> range(2,-1,-2) -> 2, 0

再举一个例子,[::-1] -> range(len(s)-1,-1,-1) -> range(4,-1,-1) -> 4, 3, 2, 1, 0


综上所述

enter image description here

8赞 aguadopd 5/28/2019 #30

我不认为 Python 教程图(在其他各种答案中引用)是好的,因为这个建议适用于积极的步幅,但不适用于消极的步幅。

这是图表:

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

从图中可以看出,我希望是这样,但事实并非如此。a[-4,-6,-1]yPty

>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'

总是有效的是思考字符或插槽,并使用索引作为半开间隔 - 如果正步幅,则右开,如果负步幅,则左开。

这样,我就可以想到区间术语。a[-4:-6:-1]a(-6,-4]

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

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

评论

0赞 aguadopd 7/20/2021
今天使用 2021/07/19 由我自己, qué capo aguadopd del pasado
1赞 CoolHandLouis 1/9/2022
作为一个新手,这是一种有趣的思考方式。但是,最后一个示例,从 -6、-5、-4、-3、-2、-1、0、1、2、3、4、5 计数有点误导,因为字符串不是那样加倍的。此外,可以引用正位置和否定位置,如下所示:a[-4:-6:-1] 与 a[-4:0:-1] 相同,因为第 0 个位置与第 -6 个位置相同。所以我会删除/忽略那个例子。
4赞 grbruns 6/14/2019 #31

我有点沮丧,因为没有找到在线资源,或者没有找到准确描述切片作用的 Python 文档。

我采纳了 Aaron Hall 的建议,阅读了 CPython 源代码的相关部分,并编写了一些 Python 代码,其执行切片的方式与 CPython 中的切片方式类似。我已经在 Python 3 中对整数列表进行了数百万次随机测试。

您可能会发现我的代码中对 CPython 中相关函数的引用很有帮助。

def slicer(x, start=None, stop=None, step=None):
    """ Return the result of slicing list x.  

    See the part of list_subscript() in listobject.c that pertains 
    to when the indexing item is a PySliceObject.
    """

    # Handle slicing index values of None, and a step value of 0.
    # See PySlice_Unpack() in sliceobject.c, which
    # extracts start, stop, step from a PySliceObject.
    maxint = 10000000       # A hack to simulate PY_SSIZE_T_MAX
    if step is None:
        step = 1
    elif step == 0:
        raise ValueError('slice step cannot be zero')

    if start is None:
        start = maxint if step < 0 else 0
    if stop is None:
        stop = -maxint if step < 0 else maxint

    # Handle negative slice indexes and bad slice indexes.
    # Compute number of elements in the slice as slice_length.
    # See PySlice_AdjustIndices() in sliceobject.c
    length = len(x)
    slice_length = 0

    if start < 0:
        start += length
        if start < 0:
            start = -1 if step < 0 else 0
    elif start >= length:
        start = length - 1 if step < 0 else length

    if stop < 0:
        stop += length
        if stop < 0:
            stop = -1 if step < 0 else 0
    elif stop > length:
        stop = length - 1 if step < 0 else length

    if step < 0:
        if stop < start:
            slice_length = (start - stop - 1) // (-step) + 1
    else:
        if start < stop:
            slice_length = (stop - start - 1) // step + 1

    # Cases of step = 1 and step != 1 are treated separately
    if slice_length <= 0:
        return []
    elif step == 1:
        # See list_slice() in listobject.c
        result = []
        for i in range(stop - start):
            result.append(x[i+start])
        return result
    else:
        result = []
        cur = start
        for i in range(slice_length):
            result.append(x[cur])
            cur += step
        return result

评论

0赞 Andy 3/27/2021
我阅读了所有相关文档,发现没有对这种语法的描述。我怀疑自己的能力,看到这个答案后感到如释重负。也许没有。
18赞 Raman 8/23/2019 #32

我个人认为它就像一个循环:for

a[start:end:step]
# for(i = start; i < end; i += step)

另外,请注意,和的负值相对于列表的末尾,在上面的示例中由 计算。startendgiven_index + a.shape[0]

20赞 Anshika Singh 7/23/2020 #33

切片的规则如下:

[lower bound : upper bound : step size]

我-转换 和 变成常见标志。upper boundlower bound

第二-然后检查是值还是值。step size

(i) 如果 是正值,则应大于 ,否则打印。例如step sizeupper boundlower boundempty string

s="Welcome"
s1=s[0:3:1]
print(s1)

输出:

Wel

但是,如果我们运行以下代码:

s="Welcome"
s1=s[3:0:1]
print(s1)

它将返回一个空字符串

(ii) 如果 if 为负值,则应小于 ,否则将打印。例如:step sizeupper boundlower boundempty string

s="Welcome"
s1=s[3:0:-1]
print(s1)

输出:

cle

但是,如果我们运行以下代码:

s="Welcome"
s1=s[0:5:-1]
print(s1)

输出将是一个空字符串

因此,在代码中:

str = 'abcd'
l = len(str)
str2 = str[l-1:0:-1]    #str[3:0:-1] 
print(str2)
str2 = str[l-1:-1:-1]    #str[3:-1:-1]
print(str2)

在第一个 中,the 小于 ,因此被打印出来。str2=str[l-1:0:-1]upper boundlower bounddcb

然而,在 中,不小于 (转换为负值时,即 : 因为最后一个元素是 -1 和 3)。str2=str[l-1:-1:-1]upper boundlower boundlower bound-1index

9赞 Géry Ogam 3/28/2022 #34

关于序列索引,要记住的重要思想是

  • 非负索引从序列中的第一项开始;
  • 索引从序列中的最后一项开始(因此仅适用于有限序列)。

换言之,负索引按序列的长度向右移动:

              0   1   2   3   4   5   6   7   ...
            -------------------------
            | a | b | c | d | e | f |
            -------------------------
...  -8  -7  -6  -5  -4  -3  -2  -1

考虑到这一点,订阅切片很简单。

订阅

订阅使用以下语法:*

sequence[index]

订阅在以下位置选择单个项目:sequenceindex

>>> 'abcdef'[0]
'a'
>>> 'abcdef'[-6]
'a'

订阅引发 if 超出范围:IndexErrorindex

>>> 'abcdef'[7]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> 'abcdef'[-7]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

切片

切片使用以下语法:**

sequence[start:stop:step]

切片选择 中的一系列项目,从独占开始,到独占结束:sequencestartstop

>>> 'abcdef'[0:2:1]
'ab'
>>> 'abcdef'[0:-4:1]
'ab'
>>> 'abcdef'[-6:-4:1]
'ab'
>>> 'abcdef'[-6:2:1]
'ab'
>>> 'abcdef'[1:-7:-1]
'ba'
>>> 'abcdef'[-5:-7:-1]
'ba'

切片默认为 中 的最完整项目范围,因此,如果省略 、 、 或 :***sequencestartstopstepNone

  • step默认为1;
  • 如果为阳性step
    • start默认为 (first item index),0
    • stop默认值为 (last item index plus 1);start + len(sequence)
  • if 为负数step
    • start默认为 (last item index),-1
    • stop默认值为 (第一项索引减去 1)。start - len(sequence)
>>> 'abcdef'[0:6:1]
'abcdef'
>>> 'abcdef'[::]
'abcdef'
>>> 'abcdef'[-1:-7:-1]
'fedcba'
>>> 'abcdef'[::-1]
'fedcba'

切片引发的 if 是:ValueErrorstep0

>>> 'abcdef'[::0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: slice step cannot be zero

切片不会引发 if 或超出范围(与订阅相反):IndexErrorstartstop

>>> 'abcdef'[-7:7]
'abcdef'

* 表达式 和 是等价的。sequence[index]sequence.__getitem__(index)

** 表达式 、 和 是等效的,其中内置类切片实例包含 、 和 。sequence[start:stop:step]sequence[slice(start, stop, step)]sequence.__getitem__(slice(start, stop, step))startstopstep

表达式 、 和 使用 、 和 的缺省值。sequence[:]sequence[::]sequence[None:None:None]startstopstep

1赞 Kyle Meador 5/25/2022 #35

已经有很多答案了,但我想添加一个性能比较:

python3.8 -m timeit -s 'fun = "this is fun;slicer = slice(0, 3)"' "fun_slice = fun[slicer]"
10000000 loops, best of 5: 29.8 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "fun_slice = fun[0:3]"
10000000 loops, best of 5: 37.9 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "fun_slice = fun[slice(0, 3)]"
5000000 loops, best of 5: 68.7 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "slicer = slice(0, 3)"
5000000 loops, best of 5: 42.8 nsec per loop

因此,如果您重复使用相同的切片,则使用切片对象将有益且可读性更高。但是,如果您只切片几次,则应首选符号。[:]

-9赞 Robert 7/17/2022 #36

这里有一个简单的助记符,用于记住它是如何工作的:

  • S L *I* C *E*
  • 切片的“I”排在第一位,代表包容性
  • “E”排在最后,代表排他性。

因此将包含第 th 个元素并排除第 th 个元素。array[j:k]jk

评论

1赞 OneCricketeer 7/19/2022
你忘了喜欢.切片环绕列表step[j:k:s]
0赞 Robert 7/19/2022
但对于我所涵盖的情况,我相信我的助记符是正确和有帮助的。我不认为这在任何方面都是错误的。请重新考虑您的反对票。array[j:k]
1赞 OneCricketeer 7/19/2022
但是,您的答案并不完整,因为它没有完全回答不同的切片方式。给出的其他答案已经指定了包含/排他性细节
1赞 My Car 1/2/2023 #37

可以使用切片语法返回字符序列。

指定开始索引和结束索引(用冒号分隔)以返回字符串的一部分。

例:

获取从位置 2 到位置 5(不包括在内)的字符:

b = "Hello, World!"
print(b[2:5])

从头开始切片

通过省略起始索引,范围将从第一个字符开始:

例:

获取从开头到位置 5(不包括在内)的字符:

b = "Hello, World!"
print(b[:5])

切片到最后

通过省略结束索引,范围将结束:

例:

从位置 2 开始获取字符,一直到最后:

b = "Hello, World!"
print(b[2:])

负索引

使用负索引从字符串末尾开始切片: 例。

获取字符:

From: “World!” 中的“o”(位置 -5)

至(但不包括)“世界”中的“d!(位置 -2):

b = "Hello, World!"
print(b[-5:-2])
5赞 H.Elci 1/10/2023 #38

对于简单的方法简单易懂的方式:

在 Python 中,切片表示法 a[start:stop:step] 可用于从序列(例如列表、元组或字符串)中选择一系列元素。

起始索引是切片中包含的第一个元素,

停止索引是从切片中排除的第一个元素,也是最后一个元素

步长值是切片元素之间的索引数。

例如,请考虑以下列表:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果我们想选择 a 的所有元素,我们可以使用切片表示法 a[:]:

>>> a[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果我们想选择 a 的所有元素,但跳过所有其他元素,我们可以使用切片表示法 a[::2]:

>>> a[::2]
[0, 2, 4, 6, 8]

如果我们想选择从第三个元素(索引 2)到第七个元素(索引 6)的所有元素,我们可以使用切片表示法 a[2:7]:

>>> a[2:7]
[2, 3, 4, 5, 6]

如果我们想选择从第三个元素(索引 2)到第七个元素(索引 6)的所有元素,但跳过所有其他元素,我们可以使用切片表示法 a[2:7:2]:

>>> a[2:7:2]
[2, 4, 6]

如果我们想选择从第三个元素(索引 2)到列表末尾的所有元素,我们可以使用切片表示法 a[2:]:

>>> a[2:]
[2, 3, 4, 5, 6, 7, 8, 9]

如果我们想选择从列表开头到第七个元素(索引 6)的所有元素,我们可以使用切片表示法 a[:7]:

>>> a[:7]
[0, 1, 2, 3, 4, 5, 6]

如果你想了解更多关于切片表示法的信息,可以参考官方的 Python 文档: 链接 1 链接 2