list是一種可變的集合,其中的項目按_順序_排列。就像大多數集合一樣(參見內建的 tuple、dict 和 set),陣列可以參照任何(或多種)資料型態,包括其他陣列。如同任何序列,可以透過從左邊算起的 0-based index 編號,或從右邊算起的 -1-based index 來存取項目。陣列可以透過切片表示法或<list>.copy()來整份或部分複製。
陣列同時支援一般和可變序列操作,例如min()/max()、<list>.index()、<list>.append()和<list>.reverse()。陣列元素可以使用for item in <list>建構來疊代。當同時需要元素索引和元素值時,可以使用for index, item in enumerate(<list>)。
在底層,lists是以動態陣列實作的,類似 Java 的ArrayList型別,最常用來儲存長度未知、性質相近的一組資料(字串、數字、集合等)。陣列是非常有彈性又實用的資料結構,Python 中許多內建方法和操作都會產生陣列作為輸出。
list可以用_字面值_來宣告,兩側是方括號[],並以逗號分隔元素:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
為了方便閱讀,當list中有許多元素或巢狀資料結構時,可以加上換行:
>>> lots_of_entries = [
"Rose",
"Sunflower",
"Poppy",
"Pansy",
"Tulip",
"Fuchsia",
"Cyclamen",
"Lavender"
]
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
{"fish": "gold", "monkey": "brown", "parrot": "grey"},
("fish", "mammal", "bird"),
['water', 'jungle', 'sky']
]
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
list()建構子可以空著使用,也可以接受一個_可疊代物件_作為引數。建構子會依序走訪可疊代物件中的每個元素,並將它們加入list:
>>> no_elements = list()
>>> no_elements
[]
# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]
# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
>>> multiple_elements_from_set
[2, 3, 5, 7, 11]
用list建構子搭配string或dict時,結果可能會出乎意料:
# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")
>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')
>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}
>>> multiple_elements_dict_1 = list(source_data)
['fish', 'monkey']
由於list建構子只接受_可疊代物件_(或什麼都不接受)作為引數,_不可_疊代的物件會擲回型別錯誤。因此,改用字面值的方式來建立只有一個項目的list,會簡單得多。
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))
>>> one_element_from_iterable
[16]
陣列中的項目(以及其他序列型別str和tuple中的項目)可以透過0-based index和_括號表示法_來存取。索引可以從**left到right(從零開始),也可以從right到left**(從 -1 開始)。
| 從左側算起的索引 ⟹ |
|
⟸ 從右側算起的索引 |
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'
>>> breakfast_foods[-4]
'Oatmeal'
# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'
>>> breakfast_foods[2]
'Eggs'
# Toast is at -1
>>> breakfast_foods[-1]
'Toast'
list中某一段的元素可以透過_切片表示法_(<list>[start:stop])來存取。_切片_是指在位置index上、滿足start <= index < stop的一段元素序列。_切片_會回傳被「切出」項目的複本,不會修改原本的list。
也可以使用step參數[start:stop:step]來「跳過」或篩選list元素(例如step為 2 時,會選取範圍中每隔一個的元素):
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]
>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']
# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]
>>> primary_colors
['Red', 'Yellow', 'Blue']
對陣列使用內建的sum()函式,會回傳陣列中所有數字的總和:
>>> number_list = [1, 2, 3, 4]
>>> sum(number_list)
10
你也可以用len()函式取得陣列的_長度_:
>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
>>> len(long_list)
10
陣列也可以用各種方式合併:
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']
# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3
>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
陣列提供_疊代器_,可以像其他_序列型別_一樣用迴圈走訪。
# Looping through the list and printing out each element.
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
... print(item)
...
Orange
Green
Grey
Blue
如果想更深入了解loops和iterators,就完成loops概念吧。
伊莉絲非常期待在即將到來的拉斯維加斯之旅中玩幾局撲克(還有其他卡牌遊戲)。她是「自我追蹤」的忠實愛好者,想寫幾個小函式來幫忙處理追蹤事項,還請你一起幫她想想該怎麼做。
伊莉絲特別喜歡撲克,她想記錄自己玩了幾局,以及究竟是_哪幾局_。每一局都有自己的編號,每張牌桌也都會顯示目前正在進行的局數。伊莉絲選了一張牌桌坐下來玩她的第一局。她打算玩三局。
實作一個函式 get_rounds(<round_number>),它接受目前的局數,並回傳一個 list,內容包含該局以及接下來_即將到來的兩局_:
>>> get_rounds(27)
[27, 28, 29]
伊莉絲在第一張牌桌玩了幾局,接著休息一下,又到第二張牌桌玩了幾局……但最後每張牌桌都各拿到一份不同的陣列!她想把這兩份陣列合在一起,這樣就能在同一處追蹤所有的撲克牌局。
實作一個函式 concatenate_rounds(<rounds_1>, <rounds_2>),它接受兩個陣列,回傳一個 list,內容是第一個 list 中的所有牌局,接著是第二個 list 中的所有牌局:
>>> concatenate_rounds([27, 28, 29], [35, 36])
[27, 28, 29, 35, 36]
另一個玩家在聊到之前某幾局撲克時,提到其中兩局打起來非常相似。伊莉絲不確定自己到底有沒有玩過那幾局。
實作一個函式 list_contains_round(<rounds>, <round_number>),它接受兩個引數:一個是玩過的牌局陣列,另一個是局數。如果該局在玩過的牌局陣列裡,函式會回傳 True;否則回傳 False:
>>> list_contains_round([27, 28, 29, 35, 36], 29)
True
>>> list_contains_round([27, 28, 29, 35, 36], 30)
False
伊莉絲想試試一款叫做 Black Joe 的新遊戲。它跟 Black Jack 很像,目標是讓手上的牌加總成某個目標點數,但在 Black Joe 裡,目標是讓牌面點數的_平均值_變成 7。求平均值的方法是把所有牌面點數加起來,再除以手上牌的張數。
實作一個函式 card_average(<hand>),回傳 Black Joe 中一手牌的平均點數。
>>> card_average([5, 6, 7])
6.0
在 Black Joe 裡,速度很重要。伊莉絲想試著找出更快求出平均值的方法。
她想到兩種能得到_類平均值_數字的方法:
實作函式 approx_average_is_average(<hand>),其中 hand 是一個陣列,包含你手上每張牌的點數。
只要上述其中_一種_or兩種_策略算出的數字_等於 實際平均值,就回傳 True。
注意:所有手牌的張數都是奇數,這樣比較容易找出中位數。
>>> approx_average_is_average([1, 2, 3])
True
>>> approx_average_is_average([2, 3, 4, 8, 8])
True
>>> approx_average_is_average([1, 2, 3, 5, 9])
False
對平均值實驗的結果感到好奇,伊莉絲想知道取_偶數_位置牌的平均值,跟取_奇數_位置牌的平均值,會不會得到相同的結果。該是再寫一個測試函式的時候了!
實作一個函式 average_even_is_average_odd(<hand>),回傳一個布林值,表示偶數索引位置牌的平均值是否等於奇數索引位置牌的平均值。
>>> average_even_is_average_odd([1, 2, 3])
True
>>> average_even_is_average_odd([1, 2, 3, 4])
False
Black Joe 中每第 11 手牌都是獎勵手牌,附帶一條獎勵規則:如果你抽到的最後一張牌是傑克,就把它的點數加倍。
實作一個函式 maybe_double_last(<hand>),它接受一手牌,並檢查最後一張牌是不是傑克(11)。如果最後一張牌是傑克(11),就在回傳這手牌之前把它的點數加倍。
>>> hand = [5, 9, 11]
>>> maybe_double_last(hand)
[5, 9, 22]
>>> hand = [5, 9, 10]
>>> maybe_double_last(hand)
[5, 9, 10]