list
序列容器操作¶list
是存放序列性資料的結構。語法使用逗號 ,
分隔資料元素,用中括號(square brackets)[
]
成對包住所有元素。 list
可以是巢狀多維度的,同一個 list
中也可以存放異質類型資料,不過一般使用情境還是以同類型的資料較適合。
元素內容是按照儲存順序的 index 存取,語法為 [ index ]
。 如果按照由前往後的順序,第一個元素 index 是0,依次往後遞增; 如果反過來由後往前存取,最後一個元素 index 可以用-1,依次向前遞減。
List 範例 | 說明 |
---|---|
[] |
空的 list |
[5, 6, 7, 8] |
四個數字元素的 list |
['code', [42, 3.1415], 1.23, {}] |
巢狀、異質的 list |
list()
可以用來建構一個新的list
物件。len()
可以用來回傳容器裡的元素個數。min()
可以用來回傳容器中最小的元素。max()
可以用來回傳容器中最大的元素。list
是序列容器,可以使用序列容器的共通方法(參閱官方文件 Common Sequence Operations)。 此外,list
在建立後,元素內容可以就地變更(mutable),標準函式庫另外還有提供可以就地變更的方法,請參閱官方文件 Mutable Sequence Types,以下是幾個常用的方法:
append()
追加一個元素在容器後面。extend()
追加一系列的元素在容器後面。del L[m:n]
刪除範圍內的元素,與 L[m:n] = []
相同。copy()
產生一份複製,與 L[:]
相同。clear()
移除所有的元素,與 del L[:]
相同。insert()
插入元素到某個位置。remove()
移除第一個出現的指定元素值。pop()
回傳某個位置的元素值,並從容器中移除。sort()
對元素就地排序。reverse()
就地反轉元素順序。list
是可以 In-Place 就地變更的序列容器¶L = [123, [4, 56], 'One-Two-Three', 7.89]
print('L = {}.'.format(L))
L = [123, [4, 56], 'One-Two-Three', 7.89].
# a += b 等同於 a = a + b
L[0] += 12
print('L = {}, 1st element is changed.'.format(L))
L = [135, [4, 56], 'One-Two-Three', 7.89], 1st element is changed.
# a -= b 等同於 a = a - b
L[1][1] -= 34
print('L = {}, 2nd element is changed.'.format(L))
L = [135, [4, 22], 'One-Two-Three', 7.89], 2nd element is changed.
# a *= b 等同於 a = a * b
L[2] *= 2
print('L = {}, 3rd element is changed.'.format(L))
L = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 7.89], 3rd element is changed.
# a /= b 等同於 a = a / b
L[3] /= 0.56
print('L = {}, 4th element is changed.'.format(L))
L = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 14.089285714285712], 4th element is changed.
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
print('letters = {}, {} elements.'.format(letters, len(letters)))
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g'], 7 elements.
# 將序號 2 到 4 的元素分別用新的數值取代
letters[2:5] = [ord('c'), ord('d'), ord('e')]
print('letters = {}, {} elements.'.format(letters, len(letters)))
letters = ['a', 'b', 99, 100, 101, 'f', 'g'], 7 elements.
# 將序號 2 到 4 的元素全部用一個新的值取代
letters[2:5] = 'X'
print('letters = {}, {} elements.'.format(letters, len(letters)))
letters = ['a', 'b', 'X', 'f', 'g'], 5 elements.
# 將序號 2 到 4 的元素刪除
letters[2:5] = []
print('letters = {}, {} elements.'.format(letters, len(letters)))
letters = ['a', 'b'], 2 elements.
# 可以用空的 [] 來清空 list
letters[:] = []
print('letters = {}, {} elements.'.format(letters, len(letters)))
letters = [], 0 elements.
# 讀取超過範圍的索引會出現 IndexError
print(L[4])
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-11-27568a3683fb> in <module> 1 # 讀取超過範圍的索引會出現 IndexError ----> 2 print(L[4]) IndexError: list index out of range
# 但是 Slice 範圍超過只會被默默忽略
print('L = {}.'.format(L[:10]))
print('L reversed = {}.'.format(L[-1:-9:-1]))
L = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 14.089285714285712]. L reversed = [14.089285714285712, 'One-Two-ThreeOne-Two-Three', [4, 22], 135].
# 寫入新的 list 物件到索引結束的後面,可以直接追加元素進去
L[len(L):] = list(range(2))
print('L extended = {}, now length = {}.'.format(L, len(L)))
L extended = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 14.089285714285712, 0, 1], now length = 6.
# 寫入的 slice 索引超過結束的後面,一樣被忽略
L[len(L) + 2:] = [3, 4]
print('L extended = {}, now length = {}.'.format(L, len(L)))
L extended = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 14.089285714285712, 0, 1, 3, 4], now length = 8.
# 寫入的 slice 索引橫跨原本有和沒有的範圍,則原本有的會被覆蓋,原本沒有的範圍會新增
L[-2:] = [2, 3, 4, 5]
print('L extended = {}, now length = {}.'.format(L, len(L)))
L extended = [135, [4, 22], 'One-Two-ThreeOne-Two-Three', 14.089285714285712, 0, 1, 2, 3, 4, 5], now length = 10.
list
物件的方法 (Methods) 來操作¶針對新增、刪除、插入等 in-place 變更的操作,原則上會建議使用 list
物件提供的方法,這樣會使得程式碼可讀性比較高。
# 刪除所有元素
L.clear()
print('L = {}, length = {}'.format(L, len(L)))
L = [], length = 0
# 新增一系列元素
L.extend(range(3))
print('L = {}, length = {}'.format(L, len(L)))
L = [0, 1, 2], length = 3
# 新增一個元素,注意和 extend() 方法有甚麼差異
L.append(list(range(3)))
print('L = {}, length = {}'.format(L, len(L)))
L = [0, 1, 2, [0, 1, 2]], length = 4
# 刪除最後一個元素
del L[-1:]
print('L = {}, length = {}'.format(L, len(L)))
L = [0, 1, 2], length = 3
# 複製三份 L 串成新的 list 物件,注意三份都是同一個物件的參考
L2 = [L] * 3
print('\nL2 = {},\n(L2[0], L2[1], L2[2]) 和 L 是同一份參考嗎? ({}, {}, {})'
.format(L2, L2[0] is L, L2[1] is L, L2[2] is L))
L2 = [[0, 1, 2], [0, 1, 2], [0, 1, 2]], (L2[0], L2[1], L2[2]) 和 L 是同一份參考嗎? (True, True, True)
# copy() 是所謂的 shallow copy
L2copy = L2.copy()
print('\nL2copy = {},\nL2copy 和 L2 是同一份參考嗎? ({}),\nL2copy[0] 和 L 是同一份參考嗎? ({})'
.format(L2copy, L2copy is L2, L2copy[0] is L))
L2copy = [[0, 1, 2], [0, 1, 2], [0, 1, 2]], L2copy 和 L2 是同一份參考嗎? (False), L2copy[0] 和 L 是同一份參考嗎? (True)
# 既然都參考到同一個物件,有一個內容改變了,其他也會跟著變
L.insert(2, 0.5)
L.append(1.5)
print('\nL = {}\nL2 = {}\nL2copy = {}\n'.format(L, L2, L2copy))
L = [0, 1, 0.5, 2, 1.5] L2 = [[0, 1, 0.5, 2, 1.5], [0, 1, 0.5, 2, 1.5], [0, 1, 0.5, 2, 1.5]] L2copy = [[0, 1, 0.5, 2, 1.5], [0, 1, 0.5, 2, 1.5], [0, 1, 0.5, 2, 1.5]]
List 有提供 in-place 排序的方法 sort()
。另外 Python 也有一個內建函式 sorted()
可以用來排序,這個內建函式不是 in-place 排序,但通用於所有支援迭代(iterator)的物件。
# 將元素內容排序
L.sort()
print('由小到大排序 L = {}'.format(L))
由小到大排序 L = [0, 0.5, 1, 1.5, 2]
L.sort(reverse=True)
print('由大到小排序 L = {}'.format(L))
由大到小排序 L = [2, 1.5, 1, 0.5, 0]
# Python 內建函式 sorted 是回傳一個新的 list 物件,不是 in-place 排序。
print('L 的內容由小到大排序,新的 list = {},原本的 L = {} 沒變'.format(sorted(L), L))
L 的內容由小到大排序,新的 list = [0, 0.5, 1, 1.5, 2],原本的 L = [2, 1.5, 1, 0.5, 0] 沒變
對於序列容器或可迭代物件 S 進行操作,並生成一個新的 list
物件。
成員的操作 | 說明 |
---|---|
[運算表示句 for x in S] |
針對每個 S 的成員 x 做運算,運算結果生成新的 list 物件 |
[運算表示句 for x in S if 條件] |
針對每個*符合條件*的成員 x 做運算,運算結果生成新的 list 物件 |
List comprehension 語法結構可組成相當豐富的條件式迭代運算
[運算表示句 for x1 in S1 if 條件1
for x2 in S2 if 條件2 ...
for xN in SN if 條件N]
# 使用 for 迴圈操作 List 容器裡的成員
L = [1, 2, 3, 4, 5]
for i in range(len(L)):
L[i] += 10
print(L)
# 使用 List Comprehension 生成新的 List 物件
L = [x + 10 for x in L]
print(L)
[11, 12, 13, 14, 15] [21, 22, 23, 24, 25]
# 條件式挑選部份成員作處理
L = list(range(10))
Lnew = [n*2 for n in L if n % 2 == 0]
Lnew
[0, 4, 8, 12, 16]
# 巢狀迴圈 + 條件式
L1 = list(range(1, 4))
L2 = list(range(3, 6))
# 一般的巢狀迴圈寫法落落長
#Lnew = []
#for x in L1:
# for y in L2:
# if x != y:
# Lnew.append((x, y))
# List comprehension 的巢狀迴圈
Lnew = [(x, y) for x in L1 for y in L2 if x != y]
Lnew
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5)]
map(function, iterable)
是一個生成函式,返回一個尋訪參數2的可迭代物件,每次的迭代都返回指定函式(參數1)循序套用到尋訪參數2成員的結果。filter(function, iterable)
是一個生成函式,返回一個尋訪參數2的可迭代物件,每次迭代只會返回符合函式(參數1)測試結果的參數2的成員。functools
模組裡的 reduce(function, iterable[, initializer])
累進式套用兩個參數的函式(或二元運算子)到序列的每個成員,一直到序列化簡成單一值為止。由於 map
, filter
, 以及 reduce
都需要套用函式呼叫,時常需要另外定義額外的 lambda
函式來輔助。
一般而言,使用 map
, filter
的程式效能通常比 for
迴圈快;而使用 list comprehension 通常又比 map
, filter
還快。
L = ['31', '0.3', '52', '46.5', '12', '94.7']
# 使用 List comprehension 將字串清單轉成數值清單
print([float(s) for s in L])
# 使用生成運算表示將字串清單轉成數值清單
print(list(float(s) for s in L))
# 使用 map() 將字串清單轉成數值清單
print(list(map(float, L)))
[31.0, 0.3, 52.0, 46.5, 12.0, 94.7] [31.0, 0.3, 52.0, 46.5, 12.0, 94.7] [31.0, 0.3, 52.0, 46.5, 12.0, 94.7]
# 使用 List comprehension 僅將清單裡的十進位整數字串轉成數值
print([int(s) for s in L if s.isdecimal()])
# 使用生成運算表示將清單裡的十進位整數字串轉成數值
print(list(int(s) for s in L if s.isdecimal()))
# 使用 map() + filter() 將清單裡的十進位整數字串轉成數值
print(list(map(int, filter(str.isdecimal, L))))
[31, 52, 12] [31, 52, 12] [31, 52, 12]
# List comprehension 要另外靠函式來達到 reduce 相同的效果
print(sum([float(s) for s in L]))
# 生成運算表示也要另外靠函式來達到 reduce 相同的效果
print(sum(float(s) for s in L))
# 使用 functools.reduce() 將清單裡的字串數值加總
import operator, functools
print(functools.reduce(operator.add, [float(s) for s in L]))
# operator.add 改用自定義 lambda
print(functools.reduce((lambda x, y: x + y), (float(s) for s in L)))
236.5 236.5 236.5 236.5