本系列的第二篇文章 探索 Python(2): 探索 Python 型別的層次結構 —— 瞭解物件和容器,介紹了 Python 型別的層次結構,其中包括容器物件。這篇文章演示了 tuple
,這是一個不可變的序列。本系列的第三篇文章 探索 Python(3): 探索 Python 型別的層次結構 —— 使用字串 介紹了 Python string
,這也是一個不可變的序列,但僅針對字元資料。作為不可變的序列,tuple
和 string
物件在建立之後便無法修改。如果需要修改其中的一個,則必須建立相應型別的新容器,以便容納新資料。本文介紹一個新的序列型別:list
,這是一個可變的序列型別,並演示以多種不同方法使用它。
Python list
在介紹 Python tuple
時,我使用了類比的方法,將其比做一個袋子,您可以在袋子中存放不同的東西。Python list
與此非常類似,因此,它的功能與袋子的功能也非常類似。但有一點是不同的,即您可以使用方括號建立 list
,如清單 1 所示。
清單 1. 在 Python 中建立一個 list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>> l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> type(l) <type 'list'> >>> el = [] # Create an empty list >>> len(el) 0 >>> sl = [1] # Create a single item list >>> len(sl) 1 >>> sl = [1,] # Create a single item list, as with a tuple >>> len(sl) 1 |
本例展示如何建立包含從 0 到 9(包括 0 和 9)的簡單 list
,以及如何建立一個空列表和一個包含單個條目的列表。如果您還記得的話,建立單個條目的 tuple
還需要在單個條目後面跟一個逗號。這是區分單個條目 tuple
與方法呼叫的必要條件,這一點將在以後的文章中詳細討論。而對於 list
,則是不必要的,儘管也允許使用單個逗號。
與往常一樣,要獲取有關 Python 主題的更多資訊,您可以使用內建的幫助直譯器,例如,清單 2 展示瞭如何開始 list
類的幫助描述。
清單 2. 獲取有關 list
的幫助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>> help(list) Help on class list in module __builtin__: class list(object) | list() -> new list | list(sequence) -> new list initialized from sequence's items | | Methods defined here: | | __add__(...) | x.__add__(y) <==> x+y | | __contains__(...) | x.__contains__(y) <==> y in x | ... |
如果仔細觀察清單 2 中對 list
類的描述,您會看到其中提供了兩個不同的建構函式:一個沒有引數,另一個接受一個序列類作為引數。因此,使用建構函式及方括號簡化符號,可以建立 list
。這就提供了很大的靈活性,原因是您可以方便地將現有的序列,如 tuple
或 string
轉換為 list
,如清單 3 所示。不過,請注意,傳遞的引數必須是序列 —— 並且不只是物件序列 —— 否則將會出現錯誤。對於任何序列型別,您都可以使用 len
方法容易地查詢序列中條目的數量。
清單 3. 直接建立 list
物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
>>> l = list() >>> type(l) <type 'list'> >>> len(l) 0 >>> l [] >>> l = list((0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) # Create a list from a tuple >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> len(l) 10 >>> l = list([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # Create a list from a list >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> len(l) 10 >>> l = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) # Error: Must pass in a sequence Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: list() takes at most 1 argument (10 given) >>> l = list("0123456789") # Create a list from a string >>> l ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] >>> type(l) <type 'list'> >>> len(l) 10 |
正如您看到的,建立 list
是很容易的,如果還沒有嘗試過,現在可以試一試。您不僅能夠將序列直接傳遞給建構函式,還可以將擁有元組或字串的變數傳遞給 list
建構函式。
很明顯,序列較為有用的主要原因是它可以非常方便地訪問序列中的條目。如果還記得對 tuple
的討論,便知道可以在序列中一次訪問一個條目或者通過將條目切片來訪問條目。Python list
也可以使用相同的技術,如清單 4 所示。
清單 4. 從 list
訪問條目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>> l = list([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> l[0] # Get the first item in the list 0 >>> type(l[0]) <type 'int'> >>> l[5] # Get the sixth item in the list 5 >>> l[1:5] # Get the second through fifth items [1, 2, 3, 4] >>> type(l[1:5]) <type 'list'> >>> l[0::2] # Get every second item [0, 2, 4, 6, 8] >>> l[0], l[1], l[2] (0, 1, 2) |
在以前的文章中已經瞭解到,切片 是一個非常有用的概念,其一般形式為 l[start:end:step]
,其中 start
和 end
分別是開始和結束索引,step
是在切片時要跨過的條目數量。此外,還可以對結束索引使用負值,即從序列的結尾往回計數。另一個有用的功能是以一種很合適的方式處理錯誤(如超過序列的長度)。如前一個例子所示,您還可以選擇忽略切片中使用的三個值中的一個或多個值。例如,我在切片l[0::2]
中沒有使用結束索引。
可變的序列
在本文的開頭,我提到過 list
和 tuple
之間的主要區別在於 list
是一個可變的序列,這就意味著您不但可以方便地訪問 list
中的條目,而且可以方便地修改它們。但這會引起一個併發症狀:您只能修改序列中的條目。若要向序列中新增條目(而不僅僅是修改條目),可使用append
方法,如清單 5 所示。
清單 5. 修改 list
1 2 3 4 5 6 7 8 9 10 11 |
>>> l = [] >>> l[0] = 0 # The list is empty Traceback (most recent call last): File "<stdin>", line 1, in ? IndexError: list assignment index out of range >>> l.append(0) >>> l [0] >>> l[0] = 1 >>> l [1] |
正如前一個例子所演示的,嘗試修改不存在的 list
條目會導致出現錯誤。這一點意義重大,並演示了 Python 方法生成錯誤的情況。當問題較為嚴重時,將會產生一個錯誤,如果問題較小並且可以很容易地處理,則忽略它。
異構的可變序列
您可能想了解更為複雜的修改。通過綜合切片知識以及如何修改 list
的知識,您應該已經獲得了非常重要的見識:可以通過多種方式修改列表。就像 tuple
一樣,list
也可以持有不同型別的資料(或不同型別的物件),這就是我所說的異構的可變序列。這兩種功能在清單 6 中進行了更完整的描述。
清單 6. 異構的可變 list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
>>> l=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> l[2] = 2 >>> type(l[2]) <type 'int'> >>> l[2] = "two" # Change the type of an element >>> ,ype(l[2]) <type 'str'> >>> l [0, 1, 'two', 3, 4, 5, 6, 7, 8, 9] >>> l[2] = l[2:5] * 2 >>> l [0, 1, ['two', 3, 4, 'two', 3, 4], 3, 4, 5, 6, 7, 8, 9] >>> del(l[2]) # Remove single element >>> l [0, 1, 3, 4, 5, 6, 7, 8, 9] >>> l[1:3] = [] # Remove a slice >>> l [0, 4, 5, 6, 7, 8, 9] |
修改 list
中的條目相當容易:您可以適當地設定條目的值,甚至設定成另一種不同的型別,如 string
或另一 list
。您還可以使用重複運算子,可以將該運算子識別為乘法運算子,以便從小片段中構建更大的列表。
前面的例子向您展示瞭如何向 list
中新增元素,以及如何修改 list
中的條目。前一個例子還演示瞭如何從 list
中刪除物件。刪除條目的第一個方法是使用 del
方法。使用此方法可以刪除一個條目或一個條目範圍。您還可以使用靈活而強大的切片方法從 list
中刪除切片。
陣列
在前一個例子中您可以看到,list
可以包含另一個 list
作為條目。如果擴充套件此例子,您可能想知道每個條目由一個 list
替換將會發生什麼樣的事情。結果是一個陣列,或者從更加數學方面來講是一個矩陣。清單 7 展示瞭如何使用 list
保持二維 (2-D) 或三維 (3-D) 陣列。
清單 7. list
作為一個陣列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
>>> al = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> al [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> al[0][0] # First element in 2D array 0 >>> al[2][2] # Last element in 2D array 8 >>> al[1][2] 5 >>> al = [[[0, 1], [2, 3]], [[4, 5], [6, 7]]] >>> al [[[0, 1], [2, 3]], [[4, 5], [6, 7]]] >>> al[0][0][1] 1 >>> len(al) # Length of outer dimension 2 >>> len(al[0]) # Length of middle dimension 2 >>> len(al[0][0]) # Length of inner dimension 2 |
其他列表操作
list
物件具有許多可以應用於現有列表的有用方法。例如,您可以反轉 list
中的所有條目或排序 list
。不過,要記住這些操作的一個重點在於,它們是就地 操作,這意味著它們會修改呼叫它們所針對的 list
。因此,如果您嘗試建立新列表,並將其設定為對這些方法之一呼叫所產生的結果,則會得到一個空列表。
list
除可以用於模擬陣列外,還可以用於模擬其他資料結構。例如,append
和 pop
方法對 list
函式的操作要麼是先進先出 (FIFO) 資料結構(也稱為佇列),要麼是後進先出 (LIFO) 資料結構(也稱為堆疊)。通過允許您將條目設定為從 list
中彈出(刪除並返回),pop
方法支援這些功能。如果彈出 list
的第一項,則是一個佇列;反之,如果彈出 list
的最後一項,則是一個堆疊,如清單 8 所示。
清單 8. 操縱 list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
>>> l=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> id(l) # This is the object id for our current list 4525432 >>> l.reverse() # Reverse the list >>> l [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> id(l) # The id is the same, modified list in place. 4525432 >>> l.sort() # Sort the list in numerical order >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> id(l) # Modified the existing list 4525432 >>> l.index(5) # Same as l[5] 5 >>> l.count(0) # How many times does '0' occur in the list 1 >>> l.pop() # Take off the last item (Stack) 9 >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8] >>> l.pop(5) # Take out the fifth element 5 >>> l [0, 1, 2, 3, 4, 6, 7, 8] >>> l.pop(0) # Take the first item off the list (Queue) 0 >>> l [1, 2, 3, 4, 6, 7, 8] |
列表:切片和切塊
本文介紹了 list
,它是一個容器物件,可以方便地進行修改,而且可以持有不同型別的資料。由於它具有相當的靈活性,因此 list
是 Python 程式語言中最常用的結構之一已不足為怪。list
像一個口袋,可以容納不同型別的資料,並可以根據需要更改。您可以像使用陣列一樣使用list
,以有組織的方式容納資料;您還可以像使用佇列或堆疊一樣使用 list
。在以後的文章中還將更為深入地探索這一靈活性,並介紹強大的程式設計技術,即列表理解。