4300 字Python列表使用總結,用心!
今天列表專題的目錄如下:
很多人學習python,不知道從何學起。
很多人學習python,掌握了基本語法過後,不知道在哪裡尋找案例上手。
很多已經做案例的人,卻不知道如何去學習更加高深的知識。
那麼針對這三類人,我給大家提供一個好的學習平臺,免費領取視訊教程,電子書籍,以及課程的原始碼!
QQ群:101677771
列表基礎
-
1 建立列表
-
2 訪問元素
-
3 新增元素
-
4 刪除元素
-
5 list 與 in
-
6 list 與數字
-
7 列表生成式
-
列表進階
-
8 其他常用API
-
9 列表實現棧
-
10 列表包含自身
-
11 插入元素效能分析
-
12 深淺拷貝
-
13 列表可變性
列表基礎
1 建立列表
列表是一個容器,使用一對中括號 []
建立一個列表。
建立一個空列表:
a = [] # 空列表
建立一個含有 5 個整型元素的列表 a
:
a = [3,7,4,2,6]
列表與我們熟知的陣列很相似,但又有很大區別。
一般陣列內的元素要求同一型別,但是列表內可含有各種不同型別,包括再巢狀列表。
如下,列表 a
包含三種類型:整形,字串,浮點型:
如下列表 a
巢狀兩個列表:
2 訪問元素
列表訪問主要包括兩種:索引和切片。
如下,訪問列表 a
可通過我們所熟知的正向索引,注意從 0
開始;
也可通過Python特有的負向索引,
即從列表最後一個元素往前訪問,此時索引依次被標記為 -1,-2,...,-5
-1
開始。
除了以上通過索引訪問單個元素方式外,
還有非常像 matlab
的切片訪問方式,這是一次訪問多個元素的方法。
切片訪問的最基本結構:中間新增一個冒號。
如下切片,能一次實現訪問索引為1到4,不包括4的序列:
In [1]: a=[3,7,4,2,6]
In [2]: a[1:4]
Out[2]: [7, 4, 2]
Python支援負索引,能帶來很多便利。比如能很方便的獲取最後三個元素:
In [1]: a=[3,7,4,2,6]
In [3]: a[-3:]
Out[3]: [4, 2, 6]
除了使用一個冒號得到連續切片外,
使用兩個冒號獲取帶間隔的序列元素,兩個冒號後的數字就是間隔長度:
In [1]: a=[3,7,4,2,6]
In [7]: a[::2] # 得到切片間隔為2
Out[7]: [3, 4, 6]
其實最全的切片結構: start:stop:interval
,如下所示,獲得切片為:索引從 1
到 5
間隔為 2
:
In [6]: a=[3,7,4,2,6]
In [7]: a[1:5:2]
Out[7]: [7, 2]
3 新增元素
列表與陣列的另一個很大不同,使用陣列前,需要知道陣列長度,便於從系統中申請記憶體。
但是,列表卻不需要預先設定元素長度。
它支援任意的動態新增元素,完全不用操心列表長短。
它會隨著陣列增加或刪除而動態的調整列表大小。
這與資料結構中的線性表或向量很相似。
新增元素通常有兩類場景。
append
一次新增1個元素, insert
在指定位置新增元素:
In [8]: a=[3,7,4,2,6]
In [9]: a.append(1) # append預設在列表尾部新增元素
In [10]: a
Out[10]: [3, 7, 4, 2, 6, 1]
In [11]: a.insert(2,5) # insert 在索引2處新增元素5
In [12]: a
Out[12]: [3, 7, 5, 4, 2, 6, 1]
extend
或直接使用 +
實現一次新增多個元素:
In [13]: a.extend([0,10])# 一次就地新增0,10兩個元素
In [14]: a
Out[14]: [3, 7, 5, 4, 2, 6, 1, 0, 10]
In [15]: b = a+[11,21] # + 不是就地新增,而是重新建立一個新的列表
In [16]: b
Out[16]: [3, 7, 5, 4, 2, 6, 1, 0, 10, 11, 21]
這裡面有一個 重要細節,不知大家平時注意到嗎。
extend
方法實現批量新增元素時未建立一個新的列表,而是直接新增在原列表中,這被稱為 in-place
,就地。而 b=a+list物件
實際是建立一個新的列表物件,所以不是就地批量新增元素。
但是, a+=一個列表物件
, +=
操作符則就會自動呼叫 extend
方法進行合併運算。大家注意這些 微妙的區別,不同場景選用不同的API,以此高效節省記憶體。
4 刪除元素
刪除元素的方法有三種: remove
, pop
, del
.
remove
直接刪除元素,若被刪除元素在列表內重複出現多次,則只刪除第一次:
In [17]: a=[1,2,3,2,4,2]
In [18]: a.remove(2)
In [19]: a
Out[19]: [1, 3, 2, 4, 2]
pop
方法若不帶引數預設刪除列表最後一個元素;若帶引數則刪除此引數代表的索引處的元素:
In[19]: a = [1, 3, 2, 4, 2]
In [20]: a.pop() # 刪除最後一個元素
Out[20]: 2
In [21]: a
Out[21]: [1, 3, 2, 4]
In [22]: a.pop(1) # 刪除索引等於1的元素
Out[22]: 3
In [23]: a
Out[23]: [1, 2, 4]
del
與 pop
相似,刪除指定索引處的元素:
In [24]: a = [1, 2, 4]
In [25]: del a[1:] # 刪除索引1到最後的切片序列
In [26]: a
Out[26]: [1]
5 list 與 in
列表是可迭代的,除了使用類似 c
語言的索引遍歷外,還支援 for item in alist
這種直接遍歷元素的方法:
In [28]: a = [3,7,4,2,6]
In [29]: for item in a:
...: print(item)
3
7
4
2
6
in
與可迭代容器的結合,還用於判斷某個元素是否屬於此列表:
In [28]: a = [3,7,4,2,6]
In [30]: 4 in a
Out[30]: True
In [31]: 5 in a
Out[31]: False
6 list 與數字
內建的list與數字結合,實現元素的複製,如下所示:
In [32]: ['Hi!'] * 4
Out[32]: ['Hi!', 'Hi!', 'Hi!', 'Hi!']
表面上這種操作太方便,實際確實也很方便,比如我想快速列印20個 -
,只需下面一行程式碼:
In [33]: '-'*20
Out[33]: '--------------------'
使用列表與數字相乘構建二維列表,然後第一個元素賦值為 [1,2]
,第二個元素賦值為 [3,4]
,第三個元素為 [5]
:
In [34]: a = [[]] * 3
In [35]: a[0]=[1,2]
In [36]: a[1]=[3,4]
In [37]: a[2]=[5]
In [38]: a
Out[38]: [[1, 2], [3, 4], [5]]
7 列表生成式
列表生成式
是建立列表的一個方法,它與使用 append
等API建立列表相比,書寫更加簡潔。
使用列表生成式建立1到50的所有奇數列表:
a=[i for i in range(50) if i&1]
列表進階
8 其他常用API
除了上面提到的方法外,列表封裝的其他方法還包括如下:
clear
, index
, count
, sort
, reverse
, copy
clear
用於清空列表內的所有元素 index
用於查詢裡面某個元素的索引:
In [4]: a=[1,3,7]
In [5]: a.index(7)
Out[5]: 2
count
用於統計某元素的出現次數:
In [6]: a=[1,2,3,2,2,5]
In [7]: a.count(2) # 元素2出現3次
Out[7]: 3
sort
用於元素排序,其中引數 key
定製排序規則。如下列表,其元素為元祖,根據元祖的第二個值由小到大排序:
In [8]: a=[(3,1),(4,1),(1,3),(5,4),(9,-10)]
In [9]: a.sort(key=lambda x:x[1])
In [10]: a
Out[10]: [(9, -10), (3, 1), (4, 1), (1, 3), (5, 4)]
reverse
完成列表反轉:
In [15]: a=[1,3,-2]
In [16]: a.reverse()
In [17]: a
Out[17]: [-2, 3, 1]
copy
方法在下面講深淺拷貝時會詳細展開。
9 列表實現棧
列表封裝的這些方法,實現 棧
這個常用的資料結構比較容易。棧是一種只能在列表一端進出的特殊列表, pop
方法正好完美實現:
In [23]: stack=[1,3,5]
In [24]: stack.append(0) # push元素0到尾端,不需要指定索引
In [25]: stack
Out[25]: [1, 3, 5, 0]
In [26]: stack.pop() # pop元素,不需指定索引,此時移出尾端元素
Out[26]: 0
In [27]: stack
Out[27]: [1, 3, 5]
由此可見Python的列表當做棧用,完全沒有問題,push 和 pop 操作的時間複雜度都為 O(1)
但是使用列表模擬佇列就不那麼高效了,需要藉助Python的 collections
模組中的雙端佇列 deque
實現。
10 列表包含自身
列表的賦值操作,有一個非常有意思的問題,大家不妨耐心看一下。
In [1]: a=[1,3,5]
In [2]: a[1]=a # 列表內元素指向自身
這樣相當於建立了一個引用自身的結構。
列印結果顯示是這樣的:
In [3]: a
Out[3]: [1, [...], 5]
中間省略號表示無限迴圈,這種賦值操作導致無限迴圈,這是為什麼?下面分析下原因。
執行 a = [1,3,5]
的時候,Python 做的事情是首先建立一個列表物件 [1, 3, 5],然後給它貼上名為 a
的標籤。
執行 a[1] = a
的時候,Python 做的事情則是把列表物件的第二個元素指向 a
所引用的列表物件本身。
執行完畢後, a
標籤還是指向原來的那個物件,只不過那個物件的結構發生了變化。
從之前的列表 [1,3,5] 變成了 [1,[...], 5],而這個[...]則是指向原來物件本身的一個引用。
如下圖所示:
可以看到形成一個環路:a[1]--->中間元素--->a[1],所以導致無限迴圈。
11 插入元素效能分析
與常規陣列需要預先指定長度不同,Python 中list不需要指定容器長度,允許我們隨意的新增刪除元素。
但是這種便捷性也會帶來一定副作用,就是插入元素的時間複雜度為O(n),而不是O(1),因為insert會導致依次移動插入位置後的所有元素。
為了加深對插入元素的理解,特意把cpython實現 insert
元素的操作原始碼拿出來。
可以清楚看到 insert
元素時,插入位置處的元素都會後移一個位置,因此插入元素的時間複雜為 O(n)
,所以凡是涉及頻繁插入刪除元素的操作,都不太適合用 list
.
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
assert((size_t)n + 1 < PY_SSIZE_T_MAX);
if (list_resize(self, n+1) < 0)
return -1;
if (where < 0) {
where += n;
if (where < 0)
where = 0;
}
if (where > n)
where = n;
items = self->ob_item;
//依次移動插入位置後的所有元素
// O(n) 時間複雜度
for (i = n; --i >= where; )
items[i+1] = items[i];
Py_INCREF(v);
items[where] = v;
return 0;
}
12 深淺拷貝
list 封裝的 copy
方法實現對列表的淺拷貝,淺拷貝只拷貝一層,具體拿例子說:
In [38]: c =[1,3,5]
In [39]: cc = c.copy()
c
和 cc
分別指向一片不同記憶體,示意圖如下:
這樣修改 cc
的第一個元素,原來 c
不受影響:
In [40]: cc[0]=10 # 修改cc第一個元素
In [41]: cc
Out[41]: [10, 3, 5]
In [42]: c # 原來 c 不受影響
Out[42]: [1, 3, 5]
但是,如果內嵌一層列表,再使用copy時只拷貝一層:
In [32]: a=[[1,3],[4,2]]
In [33]: ac = a.copy()
In [34]: ac
Out[34]: [[1, 3], [4, 2]]
上面的示意圖清晰的反映出這一點,內嵌的列表並沒有實現拷貝。因此再修改內嵌的元素時,原來的列表也會受到影響。
In [35]: ac[0][0]=10
In [36]: ac
Out[36]: [[10, 3], [4, 2]]
In [37]: a
Out[37]: [[10, 3], [4, 2]]
要想實現深度拷貝,需要使用Python模組 copy
中的 deepcopy
方法。
13 列表可變性
列表是可變的,可變的物件是不可雜湊的,不可雜湊的物件不能被對映,因此不能被用作字典的鍵。
In [51]: a=[1,3]
In [52]: d={a:'不能被雜湊'} #會丟擲如下異常
# TypeError: unhashable type: 'list'
但是,有時我們確實需要列表物件作為鍵,這怎麼辦?
可以將列表轉化為元祖,元祖是可雜湊的,所以能作為字典的鍵。
總結
以上就是列表專題的所有13個方面總結,目錄如下:
-
列表基礎
-
1 建立列表
-
2 訪問元素
-
3 新增元素
-
4 刪除元素
-
5 list 與 in
-
6 list 與數字
-
7 列表生成式
-
列表進階
-
8 其他常用API
-
9 列表實現棧
-
10 列表包含自身
-
11 插入元素效能分析
-
12 深淺拷貝
-
13 列表可變性