众所周知,我们可以通过索引值(或下标)找到一个序列类型(如字符串、列表、元组……)中的单个元素,那么,如果你想要获取一个索引范围内的元素应该怎么做?切片(slice)是一种截取索引碎片的技术。借助切片技术,我们可以非常灵活地处理序列类型的对象。一般来说,切片的作用是截取序列对象。但是,它仍然存在一些误解和高级用法,值得我们注意。因此,本文将主要和大家探讨这些内容,希望大家能够有所收获。事先说明,切片不是列表的排他操作,但由于列表最具代表性,本文仅以列表为例进行讨论。1、切片的基本用法List是Python中数据结构切片的一种非常基本也是重要的写法:[i:i+n:m];其中i是切片的起始索引值,当它在列表的第一个时可以省略;i+n为slice的结束位置,为链表末尾时可省略;m可以不提供,默认值为1,不允许为0。当m为负时,列表将被翻转。注意:这些值可以大于列表长度,不会报越界。切片的基本含义是:从序列的第i个索引开始,取权到最后n位元素,按m个区间过滤。li=[1,4,5,6,7,9,11,14,16]#下面的写法可以表示整个列表,其中X>=len(li)li[0:X]==li[0:]==li[:X]==li[:]==li[::]==li[-X:X]==li[-X:]li[1:5]==[4,5,6,7]#从1开始,取5-1个元素li[1:5:2]==[4,6]#从1开始,取5-1个元素,过滤li[-1:]==[16]#取倒数第一个元素li[-4:-2]==[9,11]#从倒数第四个取-2-(-4)=2个元素li[:-2]==li[-len(li):-2]==[1,4,5,6,7,9,11]#从头开始??,取-2-(-len(li))=7位元素#当步长为负数时,先翻转列表,然后截取li[::-1]==[16,14,11,9,7,6,5,4,1]#翻转整个listli[::-2]==[16,11,7,5,1]#翻转整个列表,然后按2个区间过滤li[:-5:-1]==[16,14,11,9]#翻转整个列表,取-5-(-len(li))=4个元素li[:-5:-3]==[16,9]#翻转整个列表,取-5-(-len(li))=4位元素,然后按3个区间过滤#切片的步长不能为0li[::0]#报错(ValueError:slicestepcannotbezero)上面的一些例子是给初学者的(即使是很多老手),可能不太容易理解。我个人总结了两条经验:(1)牢牢记住公式[i:i+n:m],出现默认值时,通过想象完成公式;(2)索引为负,步长为正时,索引位置计算为倒数;当索引为负且步长为负时,先翻转列表,然后计算索引位置作为倒数。2.切片是一个伪独立的对象。切片操作的返回结果是一个新的独立序列。以列表为例,切片后得到的列表还是一个列表,占用了新的内存地址。当切片的结果被取回时,它是一个独立的对象,因此可以用于赋值操作和其他传值场景。但是slice只是浅拷贝,复制的是原list中元素的引用,所以当有变长对象的元素时,新的list会以原list为准。li=[1,2,3,4]ls=li[::]li==ls#Trueid(li)==id(ls)#Falseli.append(li[2:4])#[1,2,3,4,[3,4]]ls.extend(ls[2:4])#[1,2,3,4,3,4]#下面的例子相当于判断li的长度是否大于8if(li[8:]):print("notempty")else:print("empty")#切片列表以原列表为准lo=[1,[1,1],2,3]lp=lo[:2]#[1,[1,1]]lo[1].append(1)#[1,[1,1,1],2,3]lp#[1,[1,1,1]]由于visibility,取出slice结果,可以作为一个独立的对象,但也要注意变量的元素是否-length对象被取出。3.切片可以作为占位符切片可以作为一个独立的对象从原始序列中“取出”,也可以留在原始序列中作为占位符。三种格式化类(即%、format()、template)的拼接方式都使用了占位符的思想。对于列表,使用切片作为占位符也可以达到拼接列表的效果。特别需要注意的是,分配给切片的值必须是可迭代对象。li=[1,2,3,4]#在开头连接li[:0]=[0]#[0,1,2,3,4]#在结尾连接li[len(li):]=[5,7]#[0,1,2,3,4,5,7]#中间拼接li[6:6]=[6]#[0,1,2,3,4,5,6,7]#给切片赋值必须是一个可迭代对象li[-1:-1]=6#(error,TypeError:canonlyassignaniterable)li[:0]=(9,)#[9,0,1,2,3,4,5,6,7]li[:0]=范围(3)#[0,1,2,9,0,1,2,3,4,5,6,7]在上面的例子中,如果把切片作为独立对象取出来,你会发现它们都是空列表,即li[:0]==li[len(li):]==li[6:6]==[],我称这种占位符为“纯占位符”。给纯占位符赋值不会破坏原有元素,只会在特定索引位置拼接新元素。删除纯占位符时,列表中的元素也不受影响。对应“纯占位符”,“非纯占位符”的切片是一个非空列表,对它的操作(赋值和删除)会影响到原列表。如果纯占位符可以实现列表的拼接,那么非纯占位符就可以实现列表替换。li=[1,2,3,4]#不同位置的替换li[:3]=[7,8,9]#[7,8,9,4]li[3:]=[5,6,7]#[7,8,9,5,6,7]li[2:4]=['a','b']#[7,8,'a','b',6,7]#非等长替换li[2:4]=[1,2,3,4]#[7,8,1,2,3,4,6,7]li[2:6]=['a']#[7,8,'a',6,7]#删除元素delli[2:3]#[7,8,6,7]切片占位符可以有步长,从而实现连续跨越替换或删除效果。需要注意的是,这种用法只支持等长替换。li=[1,2,3,4,5,6]li[::2]=['a','b','c']#['a',2,'b',4,'c',6]li[::2]=[0]*3#[0,2,0,4,0,6]li[::2]=['w']#错误,尝试分配序列大小为1的扩展切片大小为3delli[::2]#[2,4,6]4.多想想其他编程语言有没有类似Python的切片操作?有什么不同?我发现Go中的切片是一种奇怪的设计。首先,它是一种特殊类型,即对数组(array)进行切片后,结果不是数组;其次,可以创建和初始化一个切片,需要声明长度(len)和容量(cap);而且,它还有一个动态机制,需要扩展到底层数组的限制之外,这有点类似于Python列表的过度分配机制......在我看来,无论是意图,写作还是用法都是Pythonic的。切片操作更加清晰易用。因此,本文不再做跨编程语言的比较(好吧,好吧,我承认我对其他编程语言了解不多。。。)最后还有一个问题:Python的切片底层原理是什么手术?我们可以自定义切片操作吗?以上就是本次分享的全部内容。想了解更多python知识,请前往公众号:Python编程学习圈,发“J”免费领取,每日干货分享
