【Python基礎教學】資料型態函數及方法(下)【part-9】

哈囉大家好,很感謝你點進本文章,我是一名高中生,是電腦社社團的社長,由於我並不是 Python 這方面非常精通的專家,所以若文章有些錯誤請見諒,也可向我指正錯誤。另外本文章的用意是作為電腦社社團的教材使用而編寫的。

上次我們講到了 Number、String 更深入的用法,以及介紹他們特有的函數及方法。本篇教學【part 9】繼續延續上篇尚未講完的資料型態:list、tuple、set。

列表(List)

列表,也可稱為串列(List),說到列表,就讓我們來談及一個概念:序列(Sequence)

序列,是 Python 中最基本的資料結構,是 Python 中的一種內建型態(built-in type)。

內建型態就是建構在 Python Interpreter(Python 直譯器)裡面的型態

簡單來說,內建型態就是在 Python 中不需要額外安裝模組或套件,就能直接使用的資料型態,如同:Number、String 等等。

序列中的每個值都具有對應的位置值,稱為索引(index)。所有索引的開頭都預設為 0 開始,慢慢疊加上去,如:0、1、2、3 等。

在 Python 中,內建 6 個序列的內建型態,而最常見的就是列表(list)跟元組(tuple)。

定義(definition)


列表(list)是一種常見的內建資料結構。列表是一種有序的集合,可以包含不同資料型態的元素(element)。列表的元素是可變的(mutable),這表示你可以任意修改列表之內容,例如增加、刪除或修改元素。列表使用中括號 [] 定義,元素之間以逗號分隔。

以左右兩個中括號 [],括起來,裡面可以放入任意不同的資料型態,不受限,也可以在列表裡面放入一個列表,這稱為二維列表。

不過需要注意的是,在列表內每一個資料型態(元素)之間需要以逗號作為分隔,範例如下所示:

1
2
3
['s', 1234, 0.234324, [1,2,3,4,5,6]] # 這是一個列表

list1 = [1,2,3,4] # 將列表存入變數 list1 當中

註:不建議使用 list 作為變數名稱,因為 list() 是內建函數,能使資料型態變成列表(顯式轉換),如果同名的話可能導致函數失效。

運算(operation)


statement結果描述
len([1, 2, 3])3計算列表中的元素總和
[1, 2, 3] + [4, 5, 6][1, 2, 3, 4, 5, 6]以運算子 “+” 組合兩個列表
[‘Hi!’] * 4[‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’]以運算子 “*” 重複輸出同一個列表
3 in [1, 2, 3]True以運算子 “in” 檢查 3 是否在列表裡面

列表中所進行的運算與字串運算十分相似,不過本小節用意主要還是幫大家複習一些運算子的應用。

巢狀列表(nested-list)


簡單來說跟巢狀迴圈是一樣的概念,就是列表裡面在放一個列表,如下所示:

1
2
[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
# 註:以上是二維列表。

利用上述列表,我們可以結合巢狀迴圈這麼應用:

1
2
3
4
a = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
for i in range(len(a)):
for j in range(0,5):
print(a[i][j])

輸出結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

範例解析:

在變數 a 中,我們先將第一層做一個拆解:

首先 a[0] = [1,2,3,4,5],為一個列表,然後我們再進入第二層,也就是再加上一個中括號:

a[0][0] = 1,就能夠得到第一層中那個列表,那個第一層列表中的第一個元素的值。

之後就以此類推:a[0][1] = 2 等等…

我們看到 for 迴圈的地方,一樣先拆解第一層的代碼塊:

for i in range(len(a)) 的意思就是將 range(len(a)) 做迭代,而 len(a) 就是第一層的元素總和,也就是那個[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15],總共有三個,所以 range(len(a)) 就會將變數 i 從 0 遞增到 2,總共迭代三次。

首先第一次,我們會直接進入到 for 迴圈的第二層結構:for j in range(0,5)

這個代表說會迭代 0 ~ 4,並且存入變數 j 進行遞增。

第二層的第一次迭代(for j in range(0,5)):

a[0][0]

第二層的第二次迭代(for j in range(0,5)):

a[0][1]

第二層的第五次迭代(for j in range(0,5)):

a[0][4]

目前的狀況是,我們處於第一層的 for 迴圈中的第一次迭代,然而第一層的 for 迴圈裡面,還具有一層 for 迴圈,我們暫且稱他為第二層的 for 迴圈,而又在第二層 for 迴圈裡面,我們會迭代五次(從 0 ~ 4)。

因為還在第一層 for 迴圈的第一次迭代這個狀態中,所以 i 值並不會改變,從頭到尾都是 0,因此我們便可以從 [1,2,3,4,5] 裡面不斷印出數字出來。

依照這個原理,在當第二層 for 迴圈結束後,便會進回到第一層 for 迴圈進行下一次的迭代,以此類推。

巢狀列表在我們 Python 基本資料結構裡面的實作是非常重要的一環,所以才會將範例做一個解析,希望各位能夠清晰明瞭。

:::success
快速整理!
巢狀列表:列表裡面的元素之資料型態還是列表。[[1,2,3], [1,2,3], [1,2,3]]
巢狀迴圈:迴圈之下還有一個迴圈。

1
2
3
for i in range(10):
for j in range(5):
pass

以上是巢狀迴圈的範例,i = 1 時,j 會跑 5 次,i = 2 時,j 還是會跑 5 次,所以每次 i 變動時,j 就會從 0 ~ 4 遞增,執行 5 次。總體會執行 50 次。
:::

列表函數及方法(functions / methods)


Number函數
1len(list):計算列表元素總和
2max(list):回傳列表的最大值
3min(list):回傳列表的最小值
4list(seq):將序列轉換成列表資料型態
Number方法
1list.append(obj):在列表尾端新增新的物件
2list.count(obj):計算某元素在列表中出現的次數
3list.extend(seq):在列表末尾一次性追加另一個序列中的多個值(用新列表擴展原來的列表)
4list.index(obj):從列表中找出某個值第一個匹配項的索引位置
5list.insert(index, obj):將物件插入列表中,index 為欲插入該列表中的元素索引值,obj 為物件
6list.pop([index=-1]):移除列表中的一個元素,索引預設為列表的末端(-1)
7list.remove(obj):移除列表中某個值(obj)的第一個匹配項 -> 在括號內輸入指定值,python 會自動尋找指定刪除的值並刪除它,但輸入列表中不存在的值會 error
8list.reverse():反轉列表的元素索引值
9list.sort(key=None, reverse=False):對列表進行排序,若 reverse=False,則排序由小到大,否則反之。
9list.clear():清空整個列表
10list.copy():複製列表

以下是一個練習範例:

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
31
32
33
34
35
36
37
38
39
40
41
my_list = []

# append() 方法:在列表最末端中(意即從 index = -1)新增元素
my_list.append(10)
my_list.append(5)
my_list.append(8)

print("原始列表:", my_list) # [10, 5, 8]

# len() 函數計算列表中的元素總和
length = len(my_list)
print("列表長度:", length) # 3

# max() 函數回傳列表中的最大值
max_value = max(my_list)
print("列表中的最大值:", max_value) # 10

# min() 函數回傳列表中的最小值
min_value = min(my_list)
print("列表中的最小值:", min_value) # 5

# sort() 方法:對列表進行升序排序
my_list.sort()
print("排序後的列表:", my_list) # [5, 8, 10]

# reverse() 方法:反轉列表
my_list.reverse()
print("反轉後的列表:", my_list) # [10, 8, 5]

# count() 方法:計算某個元素在列表中出現的次數
count_10 = my_list.count(10)
print("列表中數字 10 出現的次數:", count_10) # 1

# pop() 方法:移除列表最末端(意即 index = -1)的元素
removed_element = my_list.pop()
print("移除的元素:", removed_element) # 5
print("移除後的列表:", my_list) # [10, 8]

# clear() 方法:清空列表
my_list.clear()
print("清空後的列表:", my_list) # []

元組(Tuple)

在 Python 中,元組與列表的用法類似,但差異之處就在於元組的值是不能夠進行修改的。

不知道各位有沒有在 C 語言中學過常數(Constant)這個概念,如有學過,那麼你只要將這概念投射到元組上即可,把它想成是一個常數版本的陣列(C 語言中,列表的功用和陣列相似(arrays),可以把陣列想成是運算速度很快的列表)。

要使用元組這個資料型態,須以小括號 () 括起來,不同於列表的中括號 []。

元組的創建基本上與列表相同,加入相應元素即可:

tup1 = (1,2,3,4,5)

創建一個空元組可這樣寫:

tup1 = ()

在這裡我們需要注意的是:元組若只有一個元素,必須加上一個逗號,否則會被當成另一個資料型態使用。

註:以下範例環境在 IDLE Shell 中進行。

1
2
3
4
5
6
7
>>> tup1 = (1)
>>> type(tup1) # 不加逗號,型態為整數(int)
<class 'int'>

>>> tup1 = (1,)
>>> type(tup1) # 加上逗號,型態為元組(tuple)
<class 'tuple'>

另外值得一提的是,元組不加上小括號也是能夠成立的:

1
2
3
4
5
6
7
>>> tup1 = "a", "b", "c", "d"
>>> type(tup1)
<class 'tuple'>

>>> tup2 = 4, 3, 2, 1
>>> type(tup2)
<class 'tuple'>

同樣地,元組的索引一樣是從 0 開始,也可以進行截取、組合。

由於前面已經敘述過多次截取、組合的內容,於是本節便不再詳細介紹。

另外使用元組的優點具以下三點:

  1. 讀取速度比列表快
  2. 占用的空間較少
  3. 資料更為安全(因為不能資料被修改)

強制修改元組內容


使用串列 list 存取 tuple 資料,修改資料後,再轉換為新的 tuple ( 注意,雖然變數名稱相同,但兩個 tuple 是完全不同的 )。

1
2
3
4
5
a = ('apple','banana','orange')
b = list(a)
b.append('grap')
a = tuple(b)
print(a) # ('apple', 'banana', 'orange', 'grap')

說明兩個 tuple 是完全不同的,是因為兩者之間(被修改前與修改後的變數 a)的數值(value)並未完全相同,且使用 id() 函數得知,兩者的記憶體位址是不一樣的:

1
2
3
4
5
6
7
8
9
>>> a = ('apple','banana','orange')
>>> id(a)
>>> 2080064319872

>>> b = list(a)
>>> b.append('grap')
>>> a = tuple(b)
>>> id(a)
>>> 2080059268272 #記憶體位址不一樣

運算(operation)


  1. len(tuple):計算元組總共所有的元素
  2. (1,2,3) + (4,5,6):兩元組可用 + 號連結。
  3. 元組可以用 * 號來進行重複複製。

那元組的運算基本上與列表都差不多,大概就是這樣而已。

至於元組的索引及擷取方式,其實也跟列表是一樣的,如下所示:

1
2
3
4
5
6
7
8
9
>>> a = (1,2,3,4,5,6,7)
>>> print(a[0])
1

>>> print(a[:0])
()

>>> print(a[:-1])
(1, 2, 3, 4, 5, 6)

集合(Set)

集合 ( set ) 就像是「只有鍵,沒有值」的字典,一個集合裡所有的鍵都不會重複,因為集合不會包含重複的資料的特性,常用來進行去除重複的字元、或判斷元素間是否有交集、聯集或差集之類的關聯性。

總之,集合是一個無序不重複的元素序列。

集合中的元素不會重複,並且可以進行交集、並集、差集等等的操作。

Python 中,要創建一個集合,與列表、元組不同的是,我們要使用大括號 {} 來表示一個集合,至於元素的部分我們就不再贅述,因為基本上都是一樣的,集合的定義如下所示:

1
2
3
parame = {value01, value02,...}
# or like this
set(value)

以下是一個範例(出自菜鳥教程):

1
2
set1 = {1, 2, 3, 4}           # 直接使用大括號創造集合
set2 = set([4, 5, 6, 7]) # 使用 set() 函數從列表創建集合

注意:建立一個空集合必須用 set() 而不是 {},因為 {} 是用來建立一個空字典。

不過作者在這邊建議:如果要建立一個集合的話就使用 set() 函數建立,否則很容易跟字典混淆。

:::success
快速整理!
集合(set):無序且「不重複(很重要)」的資料集合,如下:

1
2
3
>>> set1 = set([1,2,3,4,5,6,2,2,3])
>>> print(set1)
{1, 2, 3, 4, 5, 6}

無序(Unordered):集合中的元素沒有特定的順序,表示每次存取元素時,其排列順序可能會有所不同。
:::

運算(operation)


s.add(x):將元素 x 新增到集合 s 當中,範例如下所示:

1
2
3
4
>>> a = set((1,2,3,4))
>>> a.add(5)
>>> print(a)
{1, 2, 3, 4, 5}

還有一個方法,也可以加入元素,且參數可以是列表,元組,字典等,語法格式如下:

s.update(x):同 s.add(x),不過 x 可以有多個。

1
2
3
4
>>> a = set((1,2,3,4))
>>> a.update({5,6,7,8})
>>> print(a)
{1, 2, 3, 4, 5, 6, 7, 8}
1
2
3
4
>>> b = set((9,10,11,12,13))
>>> b.update([14,15,16,17,18])
>>> print(b)
{9, 10, 11, 12, 13, 14, 15, 16, 17, 18}

s.remove(x):將元素 x 從集合 s 移除,如果元素不存在,則會發生錯誤。

1
2
3
4
5
6
7
8
9
10
>>> a = set((1,2,3,4))
>>> a.remove(1)
>>> print(a)
{2, 3, 4}

>>> a.remove(0) # 因為在集合 a 當中找不到 0 元素存在,所以報錯。
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
a.remove(0)
KeyError: 0

另外還有一個方法也是移除集合中的元素,且如果元素不存在,不會發生錯誤。格式如下圖所示:

s.discard(x):同 s.remove(x),不過當找不到元素 x 時不會發生錯誤。

1
2
>>> a = set((1,2,3,4))
>>> a.discard(0) # 不會發生錯誤

我們也可以設定隨機刪除集合中的一個元素,語法格式如下:

s.pop():隨機刪除集合中的一個元素。

以上這些都是針對集合(Set)的新增、刪除的方法介紹,待後續將列出完整的方法表格。

交集、聯集、差集、對稱差集


image

圖源:集合 set - Python 教學 | STEAM 教育學習網

這個交集、聯集呢,跟我們在數學上學的是一樣的,只是這個概念被挪到程式語言上來用而已。

交集(數學上):A ∩ B(包含集合 A 與集合 B 共同擁有的元素)
聯集(數學上):A U B(包含集合 A 跟集合 B 的所有元素)
差集(數學上):A - B(包含集合 A,但不包含集合 B 的所有元素)
對稱差集(數學上):A△B(不包含集合 A 與集合 B 共同擁有的元素,也就是說 A ∩ B 以外的所有元素。)

說完數學上的意義,接下來讓我們回到程式語言的部分:

集合方法運算子
交集a.intersection(b)a&b
聯集a.union(b)a|b
差集a.difference(b)a-b
對稱差集a.symmetric_difference(b)a^b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = {1,2,3,4,5}
b = {3,4,5,6,7}

# 交集
print(a.intersection(b)) # {3, 4, 5}
print(a&b) # {3, 4, 5}
# 聯集
print(a.union(b)) # {1, 2, 3, 4, 5, 6, 7}
print(a|b) # {1, 2, 3, 4, 5, 6, 7}
# 差集
print(a.difference(b)) # {1, 2}
print(a-b) # {1, 2}
# 對稱差集
print(a.symmetric_difference(b)) # {1, 2, 6, 7}
print(a^b) # {1, 2, 6, 7}

以上範例、圖表源自於:集合 set - Python 教學 | STEAM 教育學習網

集合方法(methods)


方法(Method)敘述(description)
add()為集合新增元素
clear()移除集合中的所有元素
copy()複製一個集合
difference()差集:回傳多個集合的差集
difference_update()差集:移除集合中的元素,該元素在指定的集合也存在
discard()刪除集合中指定的元素
intersection()交集:回傳集合的交集
intersection_update()交集:回傳集合的交集
isdisjoint()判斷兩個集合是否包含相同的元素,如果沒有回傳 True,否則回傳 False
issubset()判斷指定集合是否為該方法參數集合的子集
issuperset()判斷方法的參數集合是否為指定集合的子集
pop()隨機移除元素
remove()移除指定元素
symmetric_difference()對稱差集:回傳兩個集合中不重複的元素集合
symmetric_difference_update()對稱差集:移除目前集合中在另外一個指定集合相同的元素,並將另一個指定集合中不同的元素插入到目前集合中
union()聯集:回傳兩個集合的聯集
update()為集合添加元素
len()計算集合元素個數

表格來源:Python3 集合 | 菜鳥教程

以下是一個練習範例:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 建立兩個集合
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}

# add(): 為集合新增元素
set1.add(9)
print("After add(9):", set1)

# clear(): 移除集合中的所有元素
temp_set = set1.copy()
temp_set.clear()
print("After clear():", temp_set)

# copy(): 複製一個集合
set_copy = set1.copy()
print("Copy of set1:", set_copy)

# difference(): 差集,回傳多個集合的差集
diff_set = set1.difference(set2)
print("Difference between set1 and set2:", diff_set)

# difference_update(): 移除集合中的元素,該元素在指定的集合也存在
set1.difference_update(set2)
print("After difference_update(set2):", set1)

# discard(): 刪除集合中指定的元素
set1.discard(9)
print("After discard(9):", set1)

# intersection(): 交集,回傳集合的交集
set_inter = set1.intersection(set2)
print("Intersection of set1 and set2:", set_inter)

# intersection_update(): 交集,回傳集合的交集
set1.intersection_update(set2)
print("After intersection_update(set2):", set1)

# isdisjoint(): 判斷兩個集合是否包含相同的元素
disjoint_result = set1.isdisjoint(set2)
print("Are set1 and set2 disjoint?:", disjoint_result)

# issubset(): 判斷指定集合是否為該方法參數集合的子集
subset_result = set1.issubset(set2)
print("Is set1 a subset of set2?:", subset_result)

# issuperset(): 判斷方法的參數集合是否為指定集合的子集
superset_result = set2.issuperset(set1)
print("Is set2 a superset of set1?:", superset_result)

# pop(): 隨機移除元素
popped_element = set1.pop()
print("Element popped from set1:", popped_element)
print("After pop():", set1)

# remove(): 移除指定元素
set2.remove(8)
print("After remove(8) from set2:", set2)

# symmetric_difference(): 對稱差集,回傳兩個集合中不重複的元素集合
sym_diff = set1.symmetric_difference(set2)
print("Symmetric difference between set1 and set2:", sym_diff)

# symmetric_difference_update(): 對稱差集
set1.symmetric_difference_update(set2)
print("After symmetric_difference_update(set2):", set1)

# union(): 聯集,回傳兩個集合的聯集
union_set = set1.union(set2)
print("Union of set1 and set2:", union_set)

# update(): 為集合添加元素
set1.update({10, 11})
print("After update({10, 11}):", set1)

# len(): 計算集合元素個數
set_length = len(set1)
print("Length of set1:", set_length)

總結

相異處列表(List)元組(Tuple)集合(Set)
有序 or 無序有序有序無序
可變 or 不可變可變(mutable)不可變(immutable)集合本身是可變的,但其元素不可變。
定義兩個中括號 [],裡面放元素(element),元素間以逗點相隔兩個小括號 (),裡面放元素(element),元素間以逗點相隔兩個大括號 {},裡面放元素(element),元素間以逗點相隔,但是元素並沒有所謂鍵值對,又稱沒有鍵值對的字典。或是可用函數 set() 建立集合。
元素資料型態任意任意不可變物件(immutable)
元素重複性可重複可重複不重複
記憶體消耗相對較高相對較低相對較低
適用情況需要有序、可變的資料結構,如佇列(Queue)、堆疊(Stack)等需要固定資料的情況,如函數參數、回傳值等需要快速查找、不重複元素的情況,如集合運算、去重複等

以下是一些參考資料~

參考資料

Python3 列表 | 菜鳥教程

Python3 元組 | 菜鳥教程

Python3 List sort()方法 | 菜鳥教程

Python序列(Sequence) - 小宇2 - 博客園

元組 ( 數組 ) tuple - Python 教學 | STEAM 教育學習網

集合 set - Python 教學 | STEAM 教育學習網

Python3 集合 | 菜鳥教程

對稱差 - 維基百科,自由的百科全書