说实在的,刚接触python切片那会儿,脑子是有点儿蒙圈的。你看那方括号里头,几个冒号隔开的数字,[start:stop:step],这都是啥意思啊?感觉像某种密码。但摸索一阵儿,特别是踩了几个坑之后,嘿,突然就明白了,这python切片简直是序列操作的瑞士军刀啊,太香了!今天就拉着你,一起把这玩意儿彻底掰扯清楚,保证你以后看到切片,再也不会发怵。

首先,得明白,切片这玩意儿主要用在Python的序列类型上,最常见的就是列表(list)、字符串(string)和元组(tuple)。它们都有个共同特点:里头的东西是有顺序的,你可以通过索引(index)来访问。比如一个列表my_list = [10, 20, 30, 40, 50]my_list[0]就是10my_list[4]就是50切片呢,就是让你一次性取出序列中的一“段”或者说一个“子序列”。

那个神秘的[start:stop:step]到底怎么解读?
* start:这代表你要切片的起始索引。记住,这个位置上的元素是包含在结果里的。如果你不写,也就是留空,它就默认从序列的开头开始切。
* stop:这是切片结束的索引。注意了!敲黑板划重点!这个位置上的元素是不包含在结果里的!它仅仅是告诉你“切到这里就停”,但停的这个位置本身是不算的。这是切片最容易让人迷糊的地方,很多初学者在这里摔过跟头(包括曾经的我)。如果你不写,默认就切到序列的末尾。
* step:这表示步长,也就是每隔多少个元素取一个。默认是1,意味着连续取。如果你写2,就是跳着取,每隔一个取一个。如果你写-1,那就是倒着取了,这个一会儿细说,可太有用了。

咱们拿个列表来练练手,感觉最直观:
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  • 最基础的:取前5个元素。你想啊,从索引0开始,到索引5结束(但5不取)。所以是numbers[0:5]。结果是[0, 1, 2, 3, 4]。看到了吧?索引5对应的5没进来。
  • 索引3到索引7之间的元素(不包含7):numbers[3:7]。结果是[3, 4, 5, 6]
  • 如果想从头开始切,比如取前5个,start可以省略:numbers[:5]。结果跟numbers[0:5]一样,[0, 1, 2, 3, 4]。方便!
  • 想从某个索引一直切到最后,比如从索引5开始到末尾:numbers[5:]。结果是[5, 6, 7, 8, 9]索引5对应的5是包含的,stop省略就是到最后。
  • 如果startstop都省略呢?numbers[:]。这不就是从头到尾嘛!结果是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。厉害的是,这种方式常常用来快速复制列表或者复制序列,得到的是一个全新的对象,而不是原来的引用。

再看看step这个参数,它让切片变得更灵活:

  • 隔一个取一个:numbers[::2]。从头到尾,步长为2。结果是[0, 2, 4, 6, 8]
  • 索引1开始,隔一个取一个:numbers[1::2]。结果是[1, 3, 5, 7, 9]
  • 索引1到索引8(不含8),隔一个取一个:numbers[1:8:2]。结果是[1, 3, 5, 7]

然后是负数索引,这玩意儿特别有Python味儿:

  • numbers[-1]:最后一个元素,9
  • numbers[-2]:倒数第二个元素,8
  • 以此类推,numbers[-len(numbers)]就是第一个元素。

结合切片负数索引,事情变得更有趣:

  • 取最后三个元素:numbers[-3:]。从倒数第三个开始到最后。结果是[7, 8, 9]
  • 取除了最后一个的所有元素:numbers[:-1]。从头开始到倒数第一个(不含倒数第一个)。结果是[0, 1, 2, 3, 4, 5, 6, 7, 8]
  • 取倒数第5个到倒数第二个(不含倒数第二个):numbers[-5:-1]。结果是[5, 6, 7, 8]。你看,哪怕是负数,start在左,stop在右,而且stop依然是不包含的。

最后,也是我觉得最酷炫的用法之一:负数步长!特别是step=-1

  • numbers[::-1]:从头到尾,步长-1。这简直就是反转列表或者反转序列终极奥义!一行代码搞定!结果是[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]。是不是比写个循环或者用reverse()方法要简洁得多?
  • 结合startstop用负数步长就更灵活了,比如你想反转索引2到索引7(不含7)这一段:numbers[6:1:-1]。注意!用负数步长时,startstop的相对位置也要“反过来”想!你要从索引6开始往回切,切到索引1的前面(不含1)。结果是[6, 5, 4, 3, 2]。是不是感觉有点儿烧脑?没关系,多试几次就习惯了。记住负数步长时,start是切片的开始,stop是切片的结束(不包含),方向是往后退。

说了这么多列表切片字符串切片元组切片呢?它们跟列表切片的语法和规则是完全一样的!这是Python序列操作的一大统一性,太赞了。

  • 字符串切片my_string = "Hello Python"

    • my_string[0:5] -> "Hello"
    • my_string[6:] -> "Python"
    • my_string[::-1] -> "nohtyP olleH"
    • my_string[::2] -> "HloPto"
  • 元组切片my_tuple = (1, 2, 3, 4, 5)

    • my_tuple[1:4] -> (2, 3, 4)
    • my_tuple[-2:] -> (4, 5)
    • my_tuple[::-1] -> (5, 4, 3, 2, 1)

看,都一样!你学会了列表切片字符串切片元组切片自然就信手拈来了。

有时候你会遇到切片超出索引范围的情况,比如一个只有5个元素的列表,你写my_list[0:10]。Python会报错吗?不会!它很聪明,会自动处理,只要startstop在合理的索引范围内(包括负数索引的映射),它都会给你返回一个合法的序列,即使是空的。比如numbers[10:]会返回一个空列表[]numbers[3:1]也会返回一个空列表[](因为起始索引大于结束索引,且步长是正的)。这是一种非常友好的设计。

为啥我觉得切片这么重要呢?除了上面说的简洁、高效,它还是一种非常“Pythonic”的思考方式。很多时候你想取出一个序列的部分,比起写循环去 append,或者用那些花哨的函数,一个简单的切片表达式就能清晰地表达你的意图。代码读起来就像自然语言一样流畅。而且,在处理大量数据时,切片通常底层实现得非常高效,比你自己写的等效循环要快得多。

最后再强调一下那个stop索引不包含的问题。你可以这样记:切片的范围是“左闭右开”的,即包含左边的start索引,但不包含右边的stop索引。或者想象成你在数栅栏,start是你开始的那个柱子,stop是你数到的最后一个柱子的下一根,你要取的是startstop之间的所有柱子,但stop这根柱子本身不属于这段。有点抽象?多写几行代码,跑几次,自然就理解了。

总而言之,怎么python切片这个问题,答案就在那个[start:stop:step]里头。掌握了这三个参数,理解了索引、特别是负数索引,以及最重要的——stop索引不包含这个规则,你就拿下了Python序列切片这座山头。多练,多用,把它变成你写Python的本能,你会发现你的代码会变得更加简洁、优雅、高效。信我,这绝对是投入时间回报率最高的一个Python基础知识点!去试试吧,用不同的列表字符串元组,各种startstopstep组合都敲一遍,亲自看看结果,那个领悟的过程比看文章更重要。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。