众所周知,咱们能够经过索引值(或称下标)来查找序列类型(如字符串、列表、元组...)中的单个元素,那么,若是要获取一个索引区间的元素该怎么办呢?python
切片(slice)就是一种截取索引片断的技术,借助切片技术,咱们能够十分灵活地处理序列类型的对象。一般来讲,切片的做用就是截取序列对象,然而,它还有一些使用误区与高级用法,都值得咱们注意。因此,本文将主要跟你们一块儿来探讨这些内容,但愿你能学有所获。编程
事先声明,切片并不是列表的专属操做,但由于列表最具备表明性,因此,本文仅以列表为例做探讨。数据结构
一、切片的基础用法app
列表是 Python 中极为基础且重要的一种数据结构,我曾写过一篇汇总文章(连接见文末)较全面地学习过它。文中详细地总结了切片的基础用法,如今回顾一下:编程语言
切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的结束位置,为列表末位时可省略;m 能够不提供,默认值是1,不容许为0 ,当m为负数时,列表翻转。注意:这些值均可以大于列表长度,不会报越界。ide
切片的基本含义是:从序列的第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位元素,按2间隔过滤 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] # 翻转整个列表 li[::-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间隔过滤 # 切片的步长不能够为0 li[::0] # 报错(ValueError: slice step cannot be zero)
上述的某些例子对于初学者(甚至不少老手)来讲,可能还很差理解。我我的总结出两条经验:(1)紧紧记住公式[i : i+n : m] ,当出现缺省值时,经过想象把公式补全;(2)索引为负且步长为正时,按倒数计算索引位置;索引为负且步长为负时,先翻转列表,再按倒数计算索引位置。code
二、切片是伪独立对象视频
切片操做的返回结果是一个新的独立的序列。以列表为例,列表切片后获得的仍是一个列表,占用新的内存地址。对象
当取出切片的结果时,它是一个独立对象,所以,能够将其用于赋值操做,也能够用于其它传递值的场景。可是,切片只是浅拷贝,它拷贝的是原列表中元素的引用,因此,当存在变长对象的元素时,新列表将受制于原列表。
''' 遇到问题没人解答?小编建立了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' li = [1, 2, 3, 4] ls = li[::] li == ls # True id(li) == id(ls) # False li.append(li[2:4]) # [1, 2, 3, 4, [3, 4]] ls.extend(ls[2:4]) # [1, 2, 3, 4, 3, 4] # 下例等价于判断li长度是否大于8 if(li[8:]): print("not empty") 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]]
因为可见,将切片结果取出,它能够做为独立对象使用,可是也要注意,是否取出了变长对象的元素。
三、切片可做为占位符
切片既能够做为独立对象被“取出”原序列,也能够留在原序列,做为一种占位符使用。
对于列表来讲,使用切片做为占位符,一样可以实现拼接列表的效果。特别须要注意的是,给切片赋值的必须是可迭代对象。
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 # (报错,TypeError: can only assign an iterable) li[:0] = (9,) # [9, 0, 1, 2, 3, 4, 5, 6, 7] li[:0] = range(3) # [0, 1, 2, 9, 0, 1, 2, 3, 4, 5, 6, 7]
上述例子中,若将切片做为独立对象取出,那你会发现它们都是空列表,即 li[:0]== li[len(li):] == li [6 : 6]==[] ,我将这种占位符称为“纯占位符”,对纯占位符赋值,并不会破坏原有的元素,只会在特定的索引位置中拼接进新的元素。删除纯占位符时,也不会影响列表中的元素。
与“纯占位符”相对应,“非纯占位符”的切片是非空列表,对它进行操做(赋值与删除),将会影响原始列表。若是说纯占位符能够实现列表的拼接,那么,非纯占位符能够实现列表的替换。
''' 遇到问题没人解答?小编建立了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' 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] # 删除元素 del li[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'] # 报错,attempt to assign sequence of size 1 to extended slice of size 3 del li[::2] # [2, 4, 6]
四、更多思考
其它编程语言是否有相似于 Python 的切片操做呢?有什么差别?