跳到主要內容

Day 10 容器型別進階操作

Day 10 容器型別進階操作

引言

Day 2 我們認識了 Python 的四大容器型別:串列、元組、字典與集合。經過一週的練習,你應該已經熟悉新增、刪除、修改、查詢這些基本動作。但在真實的程式裡,光靠基本操作往往不夠。我們經常需要「從一個串列產生出另一個串列」、「把多組資料配對起來」、「把容器當作函式的參數與回傳值」等,這些都是容器進階操作的範疇。

這篇文章會帶你認識 Python 容器型別的進階技巧:首先是「推導式(comprehension)」,這是 Python 寫出簡潔、有 Python 風格程式碼的招牌語法;接著是「解包(unpacking)」,讓你能一次取出多個值;然後介紹 enumerate() 與 zip() 這兩個走訪容器的利器;最後用一張表整理如何挑選容器型別,幫助你在實務上做出更合適的選擇。

推導式:以一個容器產生出另一個容器

推導式(comprehension)是 Python 用一行運算式產生新容器的寫法。串列推導式最常見,語法是「[運算式 for 元素 in 容器 if 條件]」,可以一次完成「篩選 + 轉換 + 收集」三件事。

# 把 1-10 的偶數平方後收集成串列
result = [n ** 2 for n in range(1, 11) if n % 2 == 0]
print(result)  # [4, 16, 36, 64, 100]

這一行做了三件事:先用 range(1, 11) 走訪 1 到 10,再用 if n % 2 == 0 篩選偶數,最後用 n ** 2 把每個偶數平方並收集起來。對照傳統寫法,推導式通常更短也更直覺。

除了串列推導式,Python 也支援字典推導式與集合推導式,語法類似但用大括號:

# 字典推導式:建立數字到平方的對應
squares = {n: n ** 2 for n in range(1, 6)}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 集合推導式:把字串中的字母去重
chars = {ch for ch in "hello world" if ch.isalpha()}
print(chars)  # {'h', 'e', 'l', 'o', 'w', 'r', 'd'}

需要注意的是,雖然推導式簡潔,但不應該無限制地巢狀下去。當推導式變得過於複雜、可讀性下降時,改用一般的 for 迴圈反而更清楚。

解包:一次取出多個值

解包(unpacking)是 Python 的另一個強大特性,能讓我們一次把容器內的多個值「拆開」指定給多個變數。它可以用在串列、元組、字典等多種情境。

# 基本解包
x, y, z = (10, 20, 30)
print(x, y, z)  # 10 20 30

# 用 * 收集剩下的值
first, *middle, last = [1, 2, 3, 4, 5]
print(first)    # 1
print(middle)   # [2, 3, 4]
print(last)     # 5

# 字典的 key / value 解包
person = {"name": "Alice", "age": 25}
for key, value in person.items():
    print(f"{key}: {value}")

解包在函式的「多重回傳值」上也很好用:函式可以用 return a, b 回傳多個值(其實是回傳一個元組),呼叫端再用同樣數量的變數接收。

def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(low, high)  # 1 9

enumerate():同時取得索引與元素

走訪容器時,如果「同時需要元素的索引與值」,與其用 range(len(...)),更推薦用 enumerate()。它會把每個元素包裝成「(索引, 值)」的元組,讓程式碼更乾淨。

fruits = ["apple", "banana", "cherry"]

# 用 enumerate 同時取得索引與值
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
# 輸出:
# 0: apple
# 1: banana
# 2: cherry

# 也可以指定起始編號
for index, fruit in enumerate(fruits, start=1):
    print(f"第 {index} 個水果是 {fruit}")

這個寫法比 for i in range(len(fruits)): fruit = fruits[i] 簡潔很多。enumerate() 在需要編號輸出的場合特別好用,例如列出待辦事項、顯示排行榜等。

zip():把多個容器配對起來

當你有兩組(或多組)對應的資料時,可以用 zip() 把它們「配對」起來一起走訪。zip() 會回傳一個迭代器,每次給出一個由各容器中對應位置的元素組成的元組。

names = ["Alice", "Bob", "Carol"]
scores = [85, 92, 78]

# 把名字與分數配對起來
for name, score in zip(names, scores):
    print(f"{name}: {score}")
# 輸出:
# Alice: 85
# Bob: 92
# Carol: 78

# 也可以搭配 list() 一次取得所有配對
pairs = list(zip(names, scores))
print(pairs)  # [('Alice', 85), ('Bob', 92), ('Carol', 78)]

注意 zip() 會以「最短的那個」容器為準,長出來的部分會被忽略。如果要保留長度差異,可以使用 itertools.zip_longest()。zip() 在資料處理中非常常見,例如合併兩個欄位、對應資料表的鍵與值等。

如何挑選容器型別

Python 的容器型別各有特性,挑選合適的容器能讓程式碼更有效率、也更容易理解。以下是簡單的挑選原則:

  • 需要有序、可修改:選用串列(list),例如學生成績、待辦事項、購物車內容。
  • 需要有序、不可修改:選用元組(tuple),例如座標、設定值、當作字典的鍵。
  • 需要快速以鍵查找值:選用字典(dict),例如使用者資料、計數表、快取。
  • 需要去除重複或集合運算:選用集合(set),例如找出唯一值、兩組資料的交集與差集。

除此之外,還有一個重要的判斷依據:「查詢速度」。對於串列,判斷某個值是否存在需要逐筆比對,時間是 O(N);對於集合與字典(以鍵查找),時間可以接近 O(1)。當資料量大、需要頻繁查詢時,集合或字典會明顯更快。

綜合練習:整理一份學生成績資料

把今天學到的工具組合起來,我們做一個小練習:用兩組串列(list)表示「學生姓名」與「對應的成績」,透過 zip() 配對,再用「字典推導式」整理成一份以姓名為鍵、成績為值的字典,最後計算平均分。

names = ["Alice", "Bob", "Carol", "Dave"]
scores = [85, 92, 78, 88]

# 用 zip + 字典推導式建立成績表
gradebook = {name: score for name, score in zip(names, scores)}
print(gradebook)
# {'Alice': 85, 'Bob': 92, 'Carol': 78, 'Dave': 88}

# 找出最高分與最低分
top_student = max(gradebook, key=gradebook.get)
low_student = min(gradebook, key=gradebook.get)
print(f"最高分:{top_student}({gradebook[top_student]} 分)")
print(f"最低分:{low_student}({gradebook[low_student]} 分)")

# 計算平均
average = sum(gradebook.values()) / len(gradebook)
print(f"平均分數:{average}")

這個範例同時用到了 zip()、字典推導式、max()/min() 搭配 key 參數、以及 sum() 與 len(),可以說是本章所學的一次總複習。實際應用中,這樣的「資料整理」流程非常常見,例如從多個欄位組成一份紀錄、從多份清單合併成字典、或計算統計資訊。

另一個延伸練習是「分組統計」:用 defaultdict(位於 collections 模組)把成績分到「及格/不及格」兩群,或用 set 找出「所有出現過的分數」。這些延伸都可以讓你更熟悉容器的選擇與組合。

巢狀推導式與注意事項

推導式也可以巢狀使用,藉此建立二維的結構。例如下面這段會建立一個 3×3 的矩陣,每個元素是其索引的乘積:

matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
for row in matrix:
    print(row)
# [1, 2, 3]
# [2, 4, 6]
# [3, 6, 9]

巢狀推導式雖然方便,但只要超過兩層就容易讓可讀性快速下降。因此有一個簡單的判斷原則:如果你需要解釋自己的推導式做了什麼,那就應該改寫成一般的 for 迴圈。Python 社群推崇「簡潔」,但更重視「可讀」,這兩者發生衝突時,永遠以可讀性為優先。

另外要小心的是,推導式的「來源容器」不應該與「目的容器」是同一個。例如下面這段會產生意料之外的結果:

nums = [1, 2, 3, 4]
squares = [n ** 2 for n in nums if n ** 2 in nums]
# 雖然不會無窮迴圈,但語意上難以一眼看懂,建議改寫

撰寫推導式時多問自己一句「這段是否一眼就能理解」,是寫出好程式碼的習慣之一。

小結:選擇容器的決策樹

把本章所有的容器知識整理起來,可以用一棵簡單的決策樹來思考:先決定是否需要有序,再決定是否需要修改,最後再決定是否需要快速查找。

  • 是否需要去重?是 → 選用集合(set)。
  • 是否需要以鍵查找值?是 → 選用字典(dict)。
  • 是否需要固定不變?是 → 選用元組(tuple)。
  • 其他情況 → 選用串列(list)。

這棵決策樹並不是絕對的鐵律,但它能幫助你在大部分情境下做出合理的選擇。隨著經驗累積,你會逐漸培養出對容器型別的直覺。

結語

今天我們認識了 Python 容器型別的進階操作:推導式讓我們用一行運算式產生新容器;解包讓我們一次取出多個值;enumerate() 與 zip() 提供了更乾淨的走訪方式;最後也整理了挑選容器型別的原則。這些技巧會讓你的程式碼更「Pythonic」,也更接近業界的實務風格。

明天,我們會進入檔案讀寫與例外處理。我們會學習如何用 open() 與 with 讀寫檔案,以及當程式出錯時,如何用 try...except 優雅地處理例外狀況,這是寫出真正穩健程式的關鍵能力。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...