切片熱身
列表的切片操作是指對其中單個或者多個索引對應元素進行的操作,具有如下幾個特點:
- 切片區間是左閉右開區間
- 切片的下標可以是負數,當為負數時,意味著從后到前的位置,且-1位倒數第一個
- 默認步長是1,可通過增加第三個參數實現不同切片
- 步長是-1時,可實現倒序切片
- 下標缺省時,表示從最"前"到最"后"(這里的前后要結合上下文來看,具體后面有示例)
例如,下面這些常規操作大家應該都很熟悉:
1lyst = list(range(10))
2lyst[1:4] #[1, 2, 3]
3lyst[1:4:2] #[1, 3]
4lyst[-5:-1] #[5, 6, 7, 8]
5lyst[-1:-5:-1] #[9, 8, 7, 6]
這里重點補充對于缺省下標的理解,即列表內部是以什么原則處理缺省下標值:
1lyst = list(range(10))
2lyst[:2] #[0, 1],起始下標缺省,默認為0,等價于lyst[0:2]
3lyst[2:] #[2, 3, 4, 5, 6, 7, 8, 9],終止下標缺省,默認為n=len(lyst),等價于lyst[2:n]
4lyst[:] #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],起始和終止下標均缺省,等價于lyst[0:n]
5lyst[:2:-1] #[9, 8, 7, 6, 5, 4, 3],步長為負數時,起始下標缺省,默認為-1,等價于lyst[-1:2:-1]
6lyst[2::-1] #[2, 1, 0],步長為負數時,終止下標缺省,默認為-n-1,等價于lyst[2:-n-1:-1]
7lyst[::-1] #[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],步長為負數,起始和終止下標均缺省,默認為lyst[-1:-n-1:-1]
總結來說,就是步長為正數時,首末缺省下標分別是0和n;步長為負時,首末缺省下標分別是-1和-n-1。特別地,當步長為-1、首末下標均缺省時,效果等價于lyst.reverse()或者reversed(lyst),但具體功能有區別:
1lyst = list(range(10))
2lyst[::-1] #輸出[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],只是輸出逆序結果,lyst本身不變
3lyst.reverse() #對列表的inplace操作,無返回值,但執行后lyst變為[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
4reversed(lyst) #lyst列表不變,返回逆序結果,但返回的是一個迭代器對象
另外,列表中切片索引數值 要求均為整數 (曾有PEP提議,索引可接受任意值,然后由python進行隱式取整處理,但被reject了),且 步長索引不能為0 。
切片訪問
對列表某索引對應值進行訪問,當對單個索引訪問時,要注意索引的合格范圍;但對列表切片時則不會顯式報錯。其中,單索引的合格范圍為-n—n-1,共2n個合格索引,其中n為列表長度;而對于范圍索引時,即使訪問越界也不會顯式報錯,而僅僅是返回結果為空:
1lyst = list(range(10))
2lyst[10] #IndexError: list index out of range
3lyst[-12] #IndexError: list index out of range
4lyst[5:15] #[5, 6, 7, 8, 9]
5lyst[10:] #[]
6lyst[-12:-1:-1] #[]
這里補充一個親身經歷的錯誤:要倒序返回一個列表的前n-1個值(即最后一個元素除外的所有元素倒序輸出),因為可以正負索引混合使用,所以自己想當然的寫下了如下語句:
1lyst[-2:-1:-1] #返回[]
我的邏輯是倒數第二個索引是-2,返回前面的所有值,第一個值是下標0,但由于索引是左開右閉區間,所以如果寫0的話訪問不到,那么要比0再小一個,也就是-1。但實際上,python可不這么想,它會將索引-2解釋為倒數第二個值沒錯,但是索引-1會解釋成倒數第一個值(更準確的講,是取不到這個值),所以上面的返回結果為空,無論步長是正還是負!
當然,實現這一需求的方法很多,只要理解了切片的索引原則:
1lyst = list(range(10))
2lyst[-2::-1] #[8, 7, 6, 5, 4, 3, 2, 1, 0],缺省下標
3lyst[:-1][::-1] #[8, 7, 6, 5, 4, 3, 2, 1, 0],先正序訪問前n-1個值再逆序
切片賦值
前面提到,列表的單索引越界訪問會報錯,切片訪問不報錯但返回結果為空。這一邏輯也類似于列表的賦值操作: 對于單索引的賦值,要求索引必須在合格范圍之內,否則報錯;但對于切片的賦值則"無需"考慮索引是否合法,甚至無需考慮賦值長度是否匹配 :
1a = [1,2,3,4,5]
2b = [5,6]
3a[8] = 8 #IndexError: list assignment index out of range
4a[-8] = 8 #IndexError: list assignment index out of range
5a[8:] = b #執行后,a為[1, 2, 3, 4, 5, 5, 6]
6a[-8:-6] = b #執行后,a為[5, 6, 1, 2, 3, 4, 5],注意這里限定了賦值區間首末
7a[-8:-10] = b #執行后,a也是[5, 6, 1, 2, 3, 4, 5],即便限定的區間實際上為空
8a[-8:] = b #執行后,a為[5, 6],因為a的賦值區間未限定長度,而賦值起始索引在a起始之前,所以整體都給覆蓋了
實際上,由于對超出列表長度的索引位置進行切片賦值會直接拼接,所以這個操作相當于列表的extend():
1a = [1,2,3,4,5]
2b = [5,6]
3a[len(a):] = b # a為[1, 2, 3, 4, 5, 5, 6]
4a.extend(b) # a也為[1, 2, 3, 4, 5, 5, 6]
既然提到了列表的extend()操作,那么下面的insert()操作不僅不會報錯,而且實際上相當于執行了append()操作:
1a = [1,2,3,4,5]
2a.insert(len(a), 100) # a為[1, 2, 3, 4, 5, 100],注意這里insert下標參數為len(a),超出合格范圍,但實際效果等價于a.append(100)
切片拷貝
由于參數引用的特殊性,python中的賦值操作或許曾令人抓狂其中而不得自拔,個人也不敢說完全理解其中的原理,所以這一部分權當是拋磚引玉。
正因為python中拷貝的特殊性,所以有個專門的庫叫copy,里面有2個重要的方法分別是copy.copy()和copy.deepcopy(),顧名思義,后者叫做深拷貝,前者自然就叫做淺拷貝。當然,這里不打算介紹這個庫和相應方法,而只是想就此引出列表中如何通過切片實現拷貝。
如果想要對一個列表進行拷貝,且后續操作互不干擾,那么簡單的直接賦值是不能完成任務的,例如執行以下語句,a和b其實管理和引用的是同一塊內存,所以操作是同步的,未實現真正的拷貝:
1a = [1,2,3,4,5]
2b = a #只是a的一個分身,未拷貝
3a[0] = 100
4b #[100, 2, 3, 4, 5]
如果想要實現a、b從此毫無瓜葛,那么簡單的拷貝實現有兩種:
1a = [1,2,3,4,5]
2b = a[:] #真正實現拷貝
3b = list(a) #也可實現拷貝
4a[0] = 100
5b #[1, 2, 3, 4, 5]
另外,再體會下這3個例子:
- 復制列表,改某個值其他不會受到影響
1a = [0]*10
2a[1] = 1 #[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
- 嵌套復制列表,牽一發動全身
1a = [[0]*10]*2
2a[1][0] = 2 #[[2, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
- 推導式+復制生成嵌套列表,改1個值其他不受影響
1a = [[0]*10 for _ in range(2)]
2a[1][0] = 2 #[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
不得不說,python里面的變量賦值與引用確實有些難以理解,這個只能靠不斷積累和嘗試,得細品!
-
參數
+關注
關注
11文章
1865瀏覽量
32770 -
元素
+關注
關注
0文章
47瀏覽量
8561 -
索引
+關注
關注
0文章
59瀏覽量
10601 -
python
+關注
關注
56文章
4822瀏覽量
85857
發布評論請先 登錄
相關推薦
Python高級特性:迭代器切片的應用
Python 切片操作的高級用法
Python學習要點:自定義序列實現切片功能
網絡切片的分類 網絡切片粒度如何選擇
5G 網絡切片之OTN切片和FlexE切片區別
Python序列的列表類型介紹
Python列表的基本概念、常用操作及實際應用
數組中如何增加切片的容量

如何對切片軟件進行操作

評論