说实在的,刚接触python切片那会儿,脑子是有点儿蒙圈的。你看那方括号里头,几个冒号隔开的数字,[start:stop:step]
,这都是啥意思啊?感觉像某种密码。但摸索一阵儿,特别是踩了几个坑之后,嘿,突然就明白了,这python切片简直是序列操作的瑞士军刀啊,太香了!今天就拉着你,一起把这玩意儿彻底掰扯清楚,保证你以后看到切片,再也不会发怵。
首先,得明白,切片这玩意儿主要用在Python的序列类型上,最常见的就是列表(list)、字符串(string)和元组(tuple)。它们都有个共同特点:里头的东西是有顺序的,你可以通过索引(index)来访问。比如一个列表my_list = [10, 20, 30, 40, 50]
,my_list[0]
就是10
,my_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
省略就是到最后。 - 如果
start
和stop
都省略呢?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()
方法要简洁得多?- 结合
start
和stop
用负数步长就更灵活了,比如你想反转索引2到索引7(不含7)这一段:numbers[6:1:-1]
。注意!用负数步长时,start
和stop
的相对位置也要“反过来”想!你要从索引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会报错吗?不会!它很聪明,会自动处理,只要start
和stop
在合理的索引范围内(包括负数索引的映射),它都会给你返回一个合法的序列,即使是空的。比如numbers[10:]
会返回一个空列表[]
,numbers[3:1]
也会返回一个空列表[]
(因为起始索引大于结束索引,且步长是正的)。这是一种非常友好的设计。
为啥我觉得切片这么重要呢?除了上面说的简洁、高效,它还是一种非常“Pythonic”的思考方式。很多时候你想取出一个序列的部分,比起写循环去 append,或者用那些花哨的函数,一个简单的切片表达式就能清晰地表达你的意图。代码读起来就像自然语言一样流畅。而且,在处理大量数据时,切片通常底层实现得非常高效,比你自己写的等效循环要快得多。
最后再强调一下那个stop
索引不包含的问题。你可以这样记:切片的范围是“左闭右开”的,即包含左边的start
索引,但不包含右边的stop
索引。或者想象成你在数栅栏,start
是你开始的那个柱子,stop
是你数到的最后一个柱子的下一根,你要取的是start
到stop
之间的所有柱子,但stop
这根柱子本身不属于这段。有点抽象?多写几行代码,跑几次,自然就理解了。
总而言之,怎么python切片这个问题,答案就在那个[start:stop:step]
里头。掌握了这三个参数,理解了索引、特别是负数索引,以及最重要的——stop
索引不包含这个规则,你就拿下了Python序列切片这座山头。多练,多用,把它变成你写Python的本能,你会发现你的代码会变得更加简洁、优雅、高效。信我,这绝对是投入时间回报率最高的一个Python基础知识点!去试试吧,用不同的列表、字符串、元组,各种start
、stop
、step
组合都敲一遍,亲自看看结果,那个领悟的过程比看文章更重要。
评论(0)