單元一:基礎鞏固與深化
1.1 型別轉換 (Casting)
型別轉換(Type Casting)是在程式執行過程中,將資料從一種型別強制轉換為另一種型別的明確操作。在Python這種動態型別語言中,雖然變數的型別是執行時期決定的,但物件本身具有固定的型別。因此,當需要進行特定運算(如數學計算)或與要求特定型別的函式庫互動時,顯式地進行型別轉換是確保程式行為符合預期的關鍵步驟。
Python 提供了多個內建函式來執行顯式型別轉換。這些函式會回傳一個代表轉換後值的新物件。
int(x): 將 x 轉換為一個整數。float(x): 將 x 轉換為一個浮點數。str(x): 將 x 轉換為一個字串。list(x): 將一個可迭代物件 x 轉換為一個列表。tuple(x): 將一個可迭代物件 x 轉換為一個元組。
進行型別轉換時,必須注意值的相容性。例如,將一個無法表示為數字的字串(如 "hello")傳遞給 int() 會引發 ValueError。
# 數值與字串互轉
num_str = "123"
num_int = int(num_str)
print(f"'{num_str}' 轉換為整數: {num_int}")
num_float = 3.14
num_str_from_float = str(num_float)
print(f"{num_float} 轉換為字串: '{num_str_from_float}'")
# 轉換失敗的例子
invalid_str = "hello"
try:
int(invalid_str)
except ValueError as e:
print(f"試圖將 '{invalid_str}' 轉換為整數時發生錯誤: {e}")
單元一:基礎鞏固與深化
1.2 變數作用域 (Scope)
作用域(Scope)是程式語言中一個基本的概念,它定義了變數、函式等識別符(Identifier)的可見性與生命週期。一個清晰的作用域規則有助於避免名稱衝突,並能有效地管理程式的狀態,是撰寫模組化與可維護程式碼的基礎。Python的名稱解析遵循著一套層級化的規則,通常稱為LEGB規則,它決定了在程式的不同位置如何尋找一個變數的定義。
LEGB 規則定義了 Python 直譯器尋找變數的順序:
- Local (L): 區域作用域,指在函式或類別方法內部。這是直譯器最先尋找的範圍。
- Enclosing (E): 閉包函式的作用域,指在巢狀函式中,外部(非全域)函式的作用域。
- Global (G): 全域作用域,指導入模組時建立的作用域,存在於單一檔案的整個生命週期。
- Built-in (B): 內建作用域,包含了 Python 內建的函式和異常,例如
print()、len()。
使用 global 和 nonlocal 關鍵字可以修改預設的作用域行為。
x = "global" # Global scope
def outer_func():
x = "enclosing" # Enclosing scope
def inner_func():
x = "local" # Local scope
print(f"Inner function sees x as: {x}")
inner_func()
print(f"Outer function sees x as: {x}")
outer_func()
print(f"Global scope sees x as: {x}")
# 修改 global 變數
def modify_global():
global x
x = "modified global"
modify_global()
print(f"After modification, global x is: {x}")
單元一:基礎鞏固與深化
1.3 字串格式化
字串格式化是一種將變數或運算式的值嵌入到字串中的技術,是產生動態輸出內容的核心方法。無論是顯示訊息給使用者、記錄日誌,還是產生設定檔,都需要依賴字串格式化來組合靜態文本與動態資料。Python提供了多種格式化方式,其中f-string因其語法直觀且效能優越,已成為現代Python程式設計的標準實踐。
常見的字串格式化方法:
- f-string (Formatted String Literals): 從 Python 3.6 開始引入,語法簡潔、可讀性高且執行效率快。以
f或F開頭,變數或運算式直接寫在{}中。 str.format()方法: 一種功能強大的方法,允許通過位置或關鍵字參數來格式化字串。- 百分號 (%) 格式化: 源自 C 語言
printf風格的舊方法,目前已不推薦在新程式碼中使用。
name = "Alice"
age = 30
pi = 3.1415926
# 1. f-string (推薦)
print(f"你好, 我是 {name}, 今年 {age} 歲。")
print(f"圓周率約為 {pi:.2f}") # 可以進行格式控制
# 2. str.format()
print("你好, 我是 {}, 今年 {} 歲。".format(name, age))
print("你好, 我是 {n}, 今年 {a} 歲。".format(n=name, a=age))
# 3. %-formatting (舊式)
print("你好, 我是 %s, 今年 %d 歲。" % (name, age))
單元一:基礎鞏固與深化
1.4 主程式入口 `if __name__ == "__main__"`
在Python中,if __name__ == '__main__': 結構是一個重要的慣例,用以區分一個檔案是被當作可執行的主程式,還是被當作模組匯入到其他程式中使用。這個機制使得程式碼可以同時具備可執行與可重用性:將主程式的執行邏輯放在此區塊內,可以確保當檔案被匯入時,這些邏輯不會被自動執行,從而維持模組的純粹性與獨立性。
當一個 Python 檔案被執行時,Python 直譯器會設定一些特殊的內建變數。其中之一就是 __name__。
- 如果檔案是直接執行的,
__name__的值會被設為"__main__"。 - 如果檔案是作為模組被匯入到另一個檔案中,
__name__的值會被設為該檔案的名稱(不含.py副檔名)。
因此,我們可以將希望只在直接執行時才運行的程式碼(例如:測試程式碼、啟動應用程式的程式碼)放在這個 if 判斷式中。
# my_module.py
def some_function():
print("Function from my_module is called.")
print(f"The value of __name__ in my_module.py is: {__name__}")
# 這段程式碼只有在直接執行 my_module.py 時才會運行
if __name__ == "__main__":
print("This script is being run directly.")
some_function()
else:
print("This script has been imported.")
# 如果你建立另一個檔案 main.py 並寫入 `import my_module`,
# 你會看到 "This script has been imported." 被印出,
# 但 "This script is being run directly." 不會。
單元一:基礎鞏固與深化
1.5 PEP 8 風格指南
PEP 8是Python官方的程式碼風格指南,旨在提高Python程式碼的一致性與可讀性。遵循一個統一的風格規範,對於軟體專案的長期維護與團隊協作至關重要。清晰、一致的程式碼更容易被理解、除錯與擴充,這也是專業軟體開發的基本要求之一。
以下透過好、壞範例的對比,來展示 PEP 8 的核心精神:
1. 命名慣例 (Naming Conventions)
壞範例 ❌
# 變數、函式命名不一致
Variable = 1
def myFunction(UserName):
# 常數使用小寫
pi = 3.14
return f"Hi, {UserName}"
好範例 ✅
# 變數、函式用 snake_case
variable = 1
def my_function(user_name):
# 常數用全大寫
PI = 3.14
return f"Hi, {user_name}"
2. 空格的使用 (Whitespace)
壞範例 ❌
x=1
y = 2
result=x+y
my_list=[1,2,3]
def func(arg1,arg2):
return arg1 * arg2
好範例 ✅
x = 1
y = 2
result = x + y
my_list = [1, 2, 3]
def func(arg1, arg2):
return arg1 * arg2
3. 匯入的順序 (Imports)
壞範例 ❌
import pandas as pd
import os
from my_project import utils
import sys
好範例 ✅
# 1. 標準函式庫
import os
import sys
# 2. 第三方套件
import pandas as pd
# 3. 本地專案模組
from my_project import utils
💡 溫馨提醒
許多整合開發環境 (IDE) 如 VS Code、PyCharm 都內建了自動格式化工具(如 Black、autopep8),可以幫助你輕鬆遵循 PEP 8 規範。
單元一:單元練習題
複習與應用
練習題 1:溫度單位轉換器
編寫一個程式,提示使用者輸入攝氏溫度(可能包含小數),然後將其轉換為華氏溫度並印出。
- 使用
input()取得字串輸入。 - 使用 型別轉換 (casting) 將輸入的字串轉為浮點數。
- 華氏溫度 = (攝氏溫度 * 9/5) + 32。
- 使用 f-string 格式化輸出結果,顯示到小數點後一位。
練習題 2:作用域觀察
觀察以下程式碼,並預測其執行後的輸出結果,以理解全域與區域作用域的差異。
message是一個全域變數。- 函式
greet內部定義了一個同名的區域變數message。 - 函式內部的修改只會影響區域變數。
練習題 3:個人化問候語產生器
編寫一個腳本,詢問使用者的姓名和出生年份,然後產生一句個人化的問候語。
- 取得使用者輸入的姓名(字串)和出生年份(字串)。
- 將出生年份轉型為整數。
- 假設當前年份為 2024,計算出使用者的年齡。
- 使用 f-string 組合出一句包含姓名和年齡的問候語。
- 將主要執行邏輯放在
if __name__ == "__main__":區塊中。
單元二:錯誤處理與測試
2.1 異常捕捉機制
異常處理是一種結構化的錯誤管理機制,允許程式在執行過程中發生錯誤時,能夠優雅地處理這些非預期的情況,而不是直接中斷執行。透過`try...except`區塊,開發者可以將可能出錯的程式碼與錯誤處理的邏輯分開,使得主程式流程更加清晰,並能針對不同類型的錯誤提供特定的應對措施,從而提升程式的穩健性(robustness)。
完整的異常處理結構包含四個關鍵字:
try: 包含可能會引發異常的程式碼區塊。except: 如果try區塊中發生了特定類型的異常,對應的except區塊將會被執行。可以有多個except區塊來處理不同類型的異常。else: 如果try區塊中沒有發生任何異常,else區塊將會被執行。finally: 無論是否發生異常,finally區塊中的程式碼總是會被執行。這通常用於釋放資源,例如關閉檔案或網路連線。
你也可以使用 raise 關鍵字主動拋出一個異常。
def divide_numbers(a, b):
try:
result = a / b
except ZeroDivisionError:
print("錯誤:除數不能為零。")
return None
except TypeError:
print("錯誤:輸入的必須是數值。")
return None
else:
print("計算成功!")
return result
finally:
print("除法運算結束。")
# 範例呼叫
print(f"結果: {divide_numbers(10, 2)}")
print("-" * 20)
print(f"結果: {divide_numbers(10, 0)}")
print("-" * 20)
print(f"結果: {divide_numbers(10, 'a')}")
單元二:錯誤處理與測試
2.2 單元測試入門
單元測試是軟體開發中的一種驗證方法,其目的是對程式中最小的功能單元(通常是函式或方法)進行獨立的測試,以確保其行為符合預期。撰寫單元測試不僅能驗證程式碼的正確性,還能作為一份可執行的文件,並在未來的修改中充當安全網,防止無意中引入的錯誤(即回歸測試),是確保軟體品質的關鍵實踐。
Python 的標準函式庫提供了 unittest 模組來支援單元測試。其核心概念包括:
- 測試案例 (Test Case): 建立一個繼承自
unittest.TestCaseの類別。 - 測試方法 (Test Method): 在測試案例類別中,定義以
test_開頭的方法。每個方法測試一個特定的功能。 - 斷言 (Assertion): 使用
TestCase類別提供的斷言方法(如assertEqual(),assertTrue(),assertRaises())來檢查結果是否符合預期。 - 執行測試: 在腳本的最後,使用
unittest.main()來啟動測試執行器。
💡 溫馨提醒
通常,測試程式碼會與原始程式碼分開存放在不同的檔案中(例如,my_functions.py 和 test_my_functions.py),這樣有助於保持專案結構的清晰。
# --- 假設這是一個獨立的檔案: calculator.py ---
def add(a, b):
return a + b
# --- 這是測試檔案: test_calculator.py ---
import unittest
# from calculator import add # 實際情況下會這樣匯入
class TestAddFunction(unittest.TestCase):
def test_add_integers(self):
"""測試兩個正整數相加"""
self.assertEqual(add(2, 3), 5)
def test_add_floats(self):
"""測試兩個浮點數相加"""
self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=7)
def test_add_negative_numbers(self):
"""測試負數相加"""
self.assertEqual(add(-1, -1), -2)
def test_type_error(self):
"""測試非數值輸入是否會引發 TypeError"""
with self.assertRaises(TypeError):
add('a', 'b')
if __name__ == '__main__':
# 為了讓此範例可獨立執行,將 add 函式定義在此
def add(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Inputs must be numeric")
return a + b
unittest.main(argv=['first-arg-is-ignored'], exit=False)
單元二:單元練習題
複習與應用
練習題 1:安全的數字輸入
編寫一個函式 `get_age()`,提示使用者輸入年齡,並持續要求輸入直到使用者提供一個有效的正整數為止。
- 使用一個無限迴圈
while True。 - 在
try區塊中,將輸入轉換為整數。 - 如果轉換成功,檢查數字是否為正數。如果是,則回傳該數字並跳出迴圈。
- 使用
except ValueError來捕捉非數字輸入的錯誤。
練習題 2:字典鍵值查詢
給定一個字典,編寫一個函式 `get_value(data_dict, key)`,該函式嘗試回傳指定鍵的值。如果鍵不存在,函式應捕捉 KeyError 並回傳一個指定的預設值。
- 在
try區塊中,直接存取data_dict[key]。 - 在
except KeyError區塊中,回傳預設值(例如 "N/A")。 - 使用
else區塊在成功找到鍵時印出一條訊息。
練習題 3:測試一個簡單的數學函式
編寫一個函式 `calculate_area(width, height)` 來計算矩形面積。然後,使用 unittest 模組為此函式編寫一個測試案例,包含至少兩個測試方法。
- 一個測試方法用來測試正整數的輸入。
- 另一個測試方法用來測試當輸入包含零或負數時,面積是否為零(或根據你的函式定義)。
- 使用
self.assertEqual()進行斷言。
單元三:檔案處理
3.1 讀寫檔案
檔案處理是程式與外部持久化(persistence)儲存(如硬碟)進行資料交換的基礎。Python透過內建的`open()`函式與檔案物件,提供了一套標準化的介面來進行檔案的讀取、寫入與其他操作。正確地管理檔案資源(例如確保檔案在使用後被關閉)是避免資料遺失或損毀的重要環節,而使用`with`陳述句是Python中處理這類資源管理的推薦方式。
open() 函式接受幾個參數,最常用的是檔案路徑和模式(mode):
'r': 讀取模式(預設值)。如果檔案不存在會引發FileNotFoundError。'w': 寫入模式。如果檔案存在,會覆蓋其內容;如果不存在,則會建立新檔案。'a': 附加模式。在檔案末尾追加內容,如果檔案不存在,則會建立新檔案。'r+': 讀寫模式。- 在模式後加上
'b'(如'rb'或'wb')可以處理二進位檔案(如圖片或音訊)。
file_path = "my_document.txt"
# 寫入檔案 (模式 'w')
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write("這是第一行。\n")
f.write("這是第二行。\n")
print(f"成功寫入檔案: {file_path}")
except IOError as e:
print(f"寫入檔案時發生錯誤: {e}")
# 附加到檔案 (模式 'a')
try:
with open(file_path, 'a', encoding='utf-8') as f:
f.write("這是附加的一行。\n")
print(f"成功附加內容至檔案: {file_path}")
except IOError as e:
print(f"附加檔案時發生錯誤: {e}")
# 讀取檔案 (模式 'r')
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
print("\n檔案內容:")
print(content)
except FileNotFoundError:
print(f"錯誤: 找不到檔案 {file_path}")
except IOError as e:
print(f"讀取檔案時發生錯誤: {e}")
💡 溫馨提醒
在處理文字檔案時,明確指定 encoding='utf-8' 是一個非常好的習慣。這可以避免在不同作業系統上因預設編碼不同而導致的亂碼問題。
單元三:檔案處理
3.2 處理 JSON 檔案
JSON(JavaScript Object Notation)是一種輕量級的資料交換格式,因其人類易讀且機器易於解析的特性,已成為Web API、設定檔及各種應用程式間資料傳輸的通用標準。Python的`json`模組提供了將Python的內建資料結構(如字典和列表)與JSON格式進行相互轉換的功能,這個過程分別稱為序列化(將Python物件轉為JSON)與反序列化(將JSON轉為Python物件)。
json 模組的核心函式有四個:
json.dump(obj, fp): 將 Python 物件(如字典、列表)序列化為 JSON 格式的字串,並寫入一個檔案物件fp。json.dumps(obj): 將 Python 物件序列化為 JSON 格式的字串並回傳。json.load(fp): 從一個檔案物件fp中讀取 JSON 資料,並將其反序列化為 Python 物件。json.loads(s): 從一個包含 JSON 資料的字串s中反序列化出 Python 物件。
import json
# 準備一個 Python 字典
data = {
"name": "John Doe",
"age": 30,
"isStudent": False,
"courses": [
{"title": "History", "credits": 3},
{"title": "Math", "credits": 4}
]
}
file_path = "data.json"
# 寫入 JSON 檔案
try:
with open(file_path, 'w', encoding='utf-8') as f:
# indent=4 讓 JSON 檔案格式化,更易讀
json.dump(data, f, indent=4, ensure_ascii=False)
print(f"成功將資料寫入 {file_path}")
except IOError as e:
print(f"寫入 JSON 檔案時發生錯誤: {e}")
# 讀取 JSON 檔案
try:
with open(file_path, 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print("\n從 JSON 檔案讀取的資料:")
print(loaded_data)
print(f"姓名: {loaded_data['name']}")
print(f"第一門課: {loaded_data['courses'][0]['title']}")
except FileNotFoundError:
print(f"錯誤: 找不到檔案 {file_path}")
except json.JSONDecodeError:
print(f"錯誤: {file_path} 的格式不正確")
單元三:檔案處理
3.3 處理 CSV 檔案
CSV(Comma-Separated Values)是一種用純文字儲存表格資料的常見格式,廣泛應用於試算表軟體與資料庫之間的資料匯入匯出。雖然格式簡單,但處理時仍需注意分隔符、引號等細節。Python的`csv`模組封裝了這些複雜性,提供了一套方便的工具,讓我們能以結構化的方式逐行讀寫CSV檔案,有效地處理表格型資料。
csv 模組的主要物件:
csv.reader(file): 建立一個 reader 物件,可以迭代讀取 CSV 檔案中的每一行,每一行都是一個字串列表。csv.writer(file): 建立一個 writer 物件,用於將資料寫入 CSV 檔案。writerow(row): 寫入單行。writerows(rows): 寫入多行。
csv.DictReader和csv.DictWriter: 功能類似,但將每一行作為字典處理,其中鍵是標頭行中的欄位名。
💡 溫馨提醒
使用 csv 模組開啟檔案時,建議在 open() 中加入 newline='' 參數。這可以防止在不同作業系統上因換行符處理方式不同而產生的空行問題。
import csv
file_path = "grades.csv"
header = ['Name', 'Subject', 'Grade']
data = [
['Alice', 'Math', 95],
['Bob', 'Science', 88],
['Charlie', 'English', 92]
]
# 寫入 CSV 檔案
try:
with open(file_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(header) # 寫入標頭
writer.writerows(data) # 寫入多行資料
print(f"成功將資料寫入 {file_path}")
except IOError as e:
print(f"寫入 CSV 檔案時發生錯誤: {e}")
# 讀取 CSV 檔案
try:
with open(file_path, 'r', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
header_read = next(reader) # 讀取並跳過標頭
print(f"\n從 CSV 檔案讀取的資料 (標頭: {header_read}):")
for row in reader:
print(f" 姓名: {row[0]}, 科目: {row[1]}, 成績: {row[2]}")
except FileNotFoundError:
print(f"錯誤: 找不到檔案 {file_path}")
except IOError as e:
print(f"讀取 CSV 檔案時發生錯誤: {e}")
單元三:單元練習題
複習與應用
練習題 1:筆記寫入與讀取
編寫一個程式,將一段多行文字寫入名為 `note.txt` 的檔案中,然後再讀取該檔案的內容並印出。
- 使用
with open(...)搭配'w'模式來寫入檔案。 - 使用
with open(...)搭配'r'模式來讀取檔案。 - 處理可能的
IOError。
練習題 2:儲存與載入設定
將一個包含使用者設定的Python字典儲存為 `config.json` 檔案,然後再讀取它,並印出其中一項設定。
- 定義一個包含設定的字典。
- 使用
json.dump()將字典寫入檔案,並使用indent=4進行美化。 - 使用
json.load()從檔案讀取回字典。 - 印出讀取後的字典中 "theme" 的值。
練習題 3:建立銷售紀錄CSV
將一個包含銷售紀錄的列表(list of lists)寫入一個名為 `sales.csv` 的檔案中,第一行為標頭。
- 定義標頭和資料。
- 使用
csv.writer。 - 先用
writerow()寫入標頭。 - 再用
writerows()寫入所有資料。 - 記得使用
newline=''。
單元四:Python GUI
4.1 桌面 GUI 套件介紹
圖形使用者介面(GUI)程式設計是一種以事件驅動(event-driven)為核心的開發模式,它允許使用者透過圖形元件(如按鈕、視窗)與程式互動。程式會進入一個事件迴圈,持續等待並回應使用者的操作。Python本身不直接提供GUI功能,而是透過各種函式庫(稱為套件)來與作業系統底層的圖形介面API互動,讓開發者能用Python語言建立桌面應用程式。
以下是一些主流的 Python GUI 套件:
-
Tkinter:
- 特點: Python 的標準 GUI 函式庫,內建於 Python 中,無需額外安裝。輕量、簡單易學,適合小型工具和初學者。
- 缺點: 預設的介面外觀較為老舊,功能相對基礎。
-
PyQt / PySide:
- 特點: 基於強大的跨平台 C++ 函式庫 Qt。功能極為豐富,可以建立複雜且美觀的現代化應用程式。提供視覺化設計工具 (Qt Designer)。
- 差異: PyQt 採用 GPL/商業授權,而 PySide(官方稱為 Qt for Python)採用更寬鬆的 LGPL 授權,對商業應用更友好。
-
wxPython:
- 特點: 基於 wxWidgets 函式庫,致力於在不同平台上提供原生(Native)的外觀和感覺。功能完整,社群活躍。
-
Kivy:
- 特點: 專為建立支援多點觸控的創新型使用者介面而設計。跨平台能力強,可以將同一份程式碼打包到 Windows, macOS, Linux, Android 和 iOS。
💡 溫馨提醒
選擇哪個套件取決於你的專案需求。如果是為了快速開發一個簡單的內部工具,Tkinter 是個不錯的起點。如果目標是開發功能複雜、介面精美的商業級應用,PyQt 或 PySide 會是更好的選擇。
單元四:Python GUI
4.2 Web-Based App 方案
相較於傳統的桌面應用,Web應用程式將使用者介面與核心邏輯分離,透過客戶端-伺服器架構,讓使用者能透過瀏覽器存取服務。這種模式具有跨平台、易於部署與更新的優點。Python擁有強大的Web開發框架生態系,從功能完整的Django、輕量靈活的Flask,到專為資料科學設計的Streamlit,提供了多樣化的解決方案來建構從簡單到複雜的Web應用。
以下是幾個主流的 Python Web-based App 解決方案:
-
Streamlit:
- 定位: 專為資料科學和機器學習專案設計,號稱是「將資料腳本轉換為可分享 Web 應用程式的最快方法」。
- 特點: 語法極其簡單,只需撰寫普通的 Python 腳本,Streamlit 會自動將其轉換為互動式網頁。內建多種元件(如滑桿、按鈕、圖表),與 Pandas、Matplotlib、Plotly 等函式庫無縫整合。
- 適用場景: 快速建立資料儀表板、模型展示、互動式報告。
-
Dash (by Plotly):
- 定位: 一個用於建立分析型 Web 應用程式的框架。
- 特點: 基於 Flask、Plotly.js 和 React.js。提供了高度的客製化能力和豐富的互動式元件。適合建立複雜、企業級的資料儀表板。
- 適用場景: 需要高度客製化和複雜互動的商業智慧(BI)儀表板、科學計算應用。
-
Flask / Django (搭配前端框架):
- 定位: 全功能的 Web 框架。Flask 輕量、靈活,被稱為微框架;Django 功能全面,自帶 ORM、管理後台,被稱為「大而全」的框架。
- 特點: 這是最傳統也最強大的 Web 開發方式。你需要分別處理後端邏輯(用 Python)和前端介面(用 HTML/CSS/JavaScript)。可以與 React, Vue, Angular 等現代前端框架結合,建立任何複雜度的網站或 Web 應用。
- 適用場景: 任何標準的網站開發,從部落格到電子商務平台。
部署平台簡介:PythonAnywhere
開發完 Web 應用後,你需要一個地方來託管它,讓其他人可以透過網路存取。PythonAnywhere 是一個專為 Python 開發者設計的線上 IDE 和網站託管平台。
- 特點: 提供一個完整的、雲端的 Python 環境。你可以直接在瀏覽器中編寫程式碼、執行腳本、管理檔案和設定資料庫。
- 部署: 部署 Flask、Django 或 Bottle 等 WSGI 框架的應用非常簡單,只需點擊幾下即可完成設定。它還支援排程任務,可以定期執行你的 Python 腳本。
- 優勢: 對於初學者和小型專案來說,它免去了自己設定伺服器和環境的複雜性,提供免費方案,是學習和展示專案的絕佳平台。
單元四:單元練習題
複習與應用
練習題 1:建立第一個 Tkinter 視窗
使用 Python 的內建 `tkinter` 函式庫,建立一個簡單的桌面應用程式視窗。
- 匯入 `tkinter` 模組。
- 建立一個主視窗物件。
- 設定視窗標題為「我的第一個GUI」。
- 在視窗中加入一個 `Label` 元件,顯示文字「歡迎來到Tkinter!」。
- 啟動主事件迴圈,讓視窗持續顯示。
練習題 2:我的第一個 Streamlit 應用
編寫一個簡單的 Python 腳本,使用 `streamlit` 建立一個互動式網頁應用。
- 安裝 `streamlit` 套件 (`pip install streamlit`)。
- 匯入 `streamlit` 模組並給予別名 `st`。
- 使用 `st.title()` 設定網頁標題。
- 使用 `st.slider()` 建立一個滑桿,讓使用者可以選擇一個 1 到 100 之間的數字。
- 在網頁上顯示使用者選擇的數字以及該數字的平方。
- 儲存為 `.py` 檔案後,在終端機使用 `streamlit run your_script_name.py` 來執行。
練習題 3:情境選擇題
根據以下兩種情境,你會選擇使用傳統桌面 GUI (如 Tkinter, PyQt) 還是 Web-based App (如 Streamlit, Dash)?請說明你的理由。
- 情境 A: 你需要開發一個小工具,讓你可以快速地批次重新命名自己電腦上某個資料夾裡的所有圖片檔案。
- 情境 B: 你需要建立一個互動式儀表板,來展示公司的銷售數據,並分享給分散在不同辦公室的團隊成員查看。
單元四:單元練習題
複習與應用
練習題 1:建立第一個 Tkinter 視窗
使用 Python 的內建 `tkinter` 函式庫,建立一個簡單的桌面應用程式視窗。
- 匯入 `tkinter` 模組。
- 建立一個主視窗物件。
- 設定視窗標題為「我的第一個GUI」。
- 在視窗中加入一個 `Label` 元件,顯示文字「歡迎來到Tkinter!」。
- 啟動主事件迴圈,讓視窗持續顯示。
練習題 2:我的第一個 Streamlit 應用
編寫一個簡單的 Python 腳本,使用 `streamlit` 建立一個互動式網頁應用。
- 安裝 `streamlit` 套件 (`pip install streamlit`)。
- 匯入 `streamlit` 模組並給予別名 `st`。
- 使用 `st.title()` 設定網頁標題。
- 使用 `st.slider()` 建立一個滑桿,讓使用者可以選擇一個 1 到 100 之間的數字。
- 在網頁上顯示使用者選擇的數字以及該數字的平方。
- 儲存為 `.py` 檔案後,在終端機使用 `streamlit run your_script_name.py` 來執行。
練習題 3:連結桌面與網路
建立一個 Tkinter 應用,視窗中有一個按鈕,點擊後可以在使用者的預設瀏覽器中打開 PythonAnywhere 的首頁。
- 匯入 `tkinter` 和 `webbrowser` 模組。`webbrowser` 是 Python 的標準函式庫,用於控制瀏覽器。
- 建立一個函式 `open_website()`,在函式內部呼叫 `webbrowser.open("https://www.pythonanywhere.com")`。
- 建立一個 `Button` 元件,並將其 `command` 參數設定為你剛建立的 `open_website` 函式。
單元五:物件導向程式設計
5.1 類別與實體
物件導向程式設計(OOP)是一種透過抽象化來組織程式碼的方法。類別(Class)是這個方法的核心,它像一個藍圖,定義了一類事物共有的屬性(資料)和行為(方法)。而實體(Instance)則是根據這個藍圖創造出來的具體個體。透過類別與實體,我們可以將複雜的問題分解為一個個獨立、可互動的物件,從而讓程式的結構更加清晰、易於管理。
在 Python 中,我們使用 class 關鍵字來定義一個類別。
- 類別 (Class): 一個抽象的模板,定義了一組屬性(Attributes)和方法(Methods)。例如,
Car是一個類別。 - 實體 (Instance) / 物件 (Object): 類別的具體實現。例如,一輛紅色的法拉利是
Car類別的一個實體。 __init__方法: 一個特殊的初始化方法(也稱為建構子),當你從類別建立一個新實體時,它會被自動呼叫。通常用於設定實體的初始狀態(屬性)。self參數: 在類別的方法中,self代表實體本身。它必須是每個實體方法的第一個參數,Python 會自動將實體傳遞給它。
class Dog:
# 類別屬性,所有實體共享
species = "Canis familiaris"
# 初始化方法 (建構子)
def __init__(self, name, age):
# 實體屬性,每個實體獨有
self.name = name
self.age = age
# 實體方法
def bark(self):
return f"{self.name} says: Woof!"
def get_info(self):
return f"Name: {self.name}, Age: {self.age}, Species: {self.species}"
# 根據 Dog 類別建立兩個實體
dog1 = Dog("Buddy", 5)
dog2 = Dog("Lucy", 3)
# 存取實體屬性
print(f"{dog1.name} is {dog1.age} years old.")
# 呼叫實體方法
print(dog1.bark())
print(dog2.get_info())
# 存取類別屬性
print(f"All dogs belong to the species: {Dog.species}")
單元五:物件導向程式設計
5.2 繼承與多型
繼承(Inheritance)與多型(Polymorphism)是物件導向程式設計中實現程式碼重用與彈性設計的兩大關鍵機制。繼承允許一個類別(子類別)基於另一個類別(父類別)來建立,從而繼承其屬性與方法,並可加以擴充或修改。多型則是指不同的物件可以對相同的操作請求(方法呼叫)做出各自不同的回應,這使得程式碼可以處理更多樣化的物件類型,增加了程式的靈活性與可擴充性。
繼承 (Inheritance)
子類別可以重用父類別的程式碼,也可以覆寫(override)父類別的方法或新增自己的方法。
使用 super() 函式可以呼叫父類別的方法,這在子類別的 __init__ 中特別有用,可以用來初始化父類別的屬性。
多型 (Polymorphism)
多型的概念是,不同的物件可以對相同的方法呼叫做出不同的反應。在 Python 中,由於其動態型別的特性(鴨子型別),多型是自然而然的。如果一個物件有某個方法(例如 speak()),你就可以呼叫它,而無需關心它到底是哪個類別的實體。
# 父類別
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement this method")
# 子類別,繼承自 Animal
class Dog(Animal):
def speak(self): # 覆寫父類別的方法
return f"{self.name} says Woof!"
# 另一個子類別
class Cat(Animal):
def speak(self): # 覆寫父類別的方法
return f"{self.name} says Meow!"
# 建立實體
my_dog = Dog("Rex")
my_cat = Cat("Whiskers")
print(my_dog.speak())
print(my_cat.speak())
# --- 多型的展現 ---
def animal_sound(animals):
for animal in animals:
# 我們不在乎 animal 是 Dog 還是 Cat,只要它有 speak 方法即可
print(animal.speak())
animal_list = [my_dog, my_cat]
print("\n--- Polymorphism Demo ---")
animal_sound(animal_list)
單元五:物件導向程式設計
5.3 封裝與特殊方法
封裝(Encapsulation)是將資料與操作該資料的方法綑綁在一起的物件導向原則,旨在隱藏物件的內部實作細節,僅對外提供有限的介面,以保護資料的完整性。特殊方法(Special Methods),在Python中以雙底線命名,它們允許我們自訂類別的行為,使其能與Python的內建操作(如加法、長度計算)無縫整合,讓自訂的物件用起來就像Python的原生型別一樣自然。
封裝 (Encapsulation)
Python 並沒有像 Java 或 C++ 那樣的 private 關鍵字來強制實現封裝。它依賴於命名慣例:
- 單底線前綴 (
_variable): 這是一種約定,告訴其他開發者這個屬性或方法是內部使用的,不應該在類別外部直接存取。但它並沒有技術上的限制。 - 雙底線前綴 (
__variable): 這會觸發 Python 的名稱修飾(Name Mangling)機制。直譯器會將名稱改為_ClassName__variable,使得從外部更難意外地存取它。
特殊方法 (Special Methods)
特殊方法讓你的自訂物件能夠像 Python 的內建物件一樣工作。
__str__(self): 當你對實體使用print()或str()時呼叫,應回傳一個易於人類閱讀的字串。__repr__(self): 當你直接在直譯器中輸入實體名稱或使用repr()時呼叫,應回傳一個明確的、能夠重建物件的官方字串表示。__len__(self): 當你對實體使用len()時呼叫。__add__(self, other): 實現+運算子。
class Book:
def __init__(self, title, author, pages):
self.title = title # 公開屬性
self.author = author # 公開屬性
self.__pages = pages # 私有屬性 (名稱修飾)
self._is_read = False # 保護屬性 (約定)
def mark_as_read(self):
self._is_read = True
print(f"'{self.title}' has been marked as read.")
def get_status(self):
return "Read" if self._is_read else "Unread"
# 特殊方法 __str__
def __str__(self):
return f"'{self.title}' by {self.author}"
# 特殊方法 __repr__
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}', pages={self.__pages})"
# 特殊方法 __len__
def __len__(self):
return self.__pages
book1 = Book("The Lord of the Rings", "J.R.R. Tolkien", 1178)
# 使用特殊方法
print(book1) # 觸發 __str__
print(str(book1)) # 觸發 __str__
print(repr(book1)) # 觸發 __repr__
print(f"The book has {len(book1)} pages.") # 觸發 __len__
# 封裝的展現
print(f"Book status: {book1.get_status()}")
book1.mark_as_read()
print(f"Book status: {book1.get_status()}")
# 嘗試存取私有屬性會失敗
try:
print(book1.__pages)
except AttributeError as e:
print(f"\nError accessing private attribute: {e}")
# 可以透過名稱修飾後的名稱存取,但不建議這麼做
print(f"Accessing mangled name: {book1._Book__pages}")
單元五:單元練習題
複習與應用
練習題 1:銀行帳戶類別
建立一個名為 `BankAccount` 的類別,用於模擬一個簡單的銀行帳戶。
__init__方法應接受 `account_holder` (戶名) 和初始 `balance` (餘額)。- 建立一個 `deposit(amount)` 方法,用於存款。
- 建立一個 `withdraw(amount)` 方法,用於提款。提款時需檢查餘額是否足夠。
- 建立一個 `get_balance()` 方法,回傳目前餘額。
練習題 2:交通工具繼承
建立一個基礎的 `Vehicle` 類別和兩個繼承它的子類別 `Car` 和 `Motorcycle`。
- `Vehicle` 類別有 `brand` (品牌) 和 `year` (年份) 屬性,以及一個 `display_info()` 方法。
- `Car` 類別繼承自 `Vehicle`,並額外擁有 `doors` (車門數) 屬性。覆寫 `display_info()` 方法以顯示所有資訊。
- `Motorcycle` 類別繼承自 `Vehicle`,並額外擁有 `cc` (排氣量) 屬性。同樣覆寫 `display_info()` 方法。
練習題 3:向量的特殊方法
建立一個 `Vector2D` 類別來表示二維向量。你需要實作特殊方法,讓向量可以被優雅地印出,並且可以使用 `+` 運算子進行相加。
__init__方法接受 `x` 和 `y` 座標。- 實作
__str__方法,讓 `print(vector)` 能印出像 `(x, y)` 這樣的格式。 - 實作
__add__方法,讓兩個向量物件可以使用 `+` 號相加,並回傳一個新的 `Vector2D` 物件。
單元六:正則表達式
6.1 正則表達式簡介
正則表達式(Regular Expression)是一套強大的字串處理工具,它使用一種特殊的語法來定義搜尋模式。透過這些模式,我們可以在文字中進行複雜的搜尋、取代與驗證操作。正則表達式在資料清理、文字探勘、日誌分析等眾多領域中都是不可或缺的技術,它能用簡潔的表達式來處理複雜的文字匹配需求。
正則表達式由普通字元(如 'a' 到 'z')和特殊字元(稱為「元字元」,metacharacters)組成。元字元賦予了正則表達式強大的模式匹配能力。
常用元字元
| 元字元 | 描述 | 範例 |
|---|---|---|
| . | 匹配除了換行符以外的任何單一字元。 | a.b 匹配 "acb", "a_b" |
| ^ | 匹配字串的開頭。 | ^Hello 匹配 "Hello world" |
| $ | 匹配字串的結尾。 | world$ 匹配 "Hello world" |
| * | 匹配前面的子表達式零次或多次。 | ab*c 匹配 "ac", "abc", "abbbc" |
| + | 匹配前面的子表達式一次或多次。 | ab+c 匹配 "abc", "abbbc", 但不匹配 "ac" |
| ? | 匹配前面的子表達式零次或一次。 | colou?r 匹配 "color" 和 "colour" |
| [] | 字元集。匹配方括號中的任何一個字元。 | [aeiou] 匹配任何一個母音 |
| () | 分組。將多個字元作為一個單元進行操作,並用於捕獲匹配的子字串。 | (ab)+ 匹配 "ab", "abab" |
| \d | 匹配任何數字,相當於 [0-9]。 |
\d{3} 匹配三個數字 |
| \w | 匹配任何字母數字字元(字母、數字、底線),相當於 [a-zA-Z0-9_]。 |
\w+ 匹配一個或多個單字字元 |
💡 溫馨提醒
在 Python 中定義正則表達式模式時,強烈建議使用原始字串(raw string),即在字串前加上 r(例如 r"\d+")。這可以防止反斜線 \ 被解釋為跳脫字元,從而簡化正則表達式的書寫。
單元六:正則表達式
6.2 常用 RegEx 函式
Python的`re`模組提供了應用正則表達式的核心函式。要有效地使用正則表達式,除了要學會撰寫模式(pattern)之外,還必須熟悉這些函式的功能,例如`search()`用於尋找第一個匹配項,`findall()`用於尋找所有匹配項,以及`sub()`用於取代。這些函式是我們在程式碼中實際運用正則表達式能力的橋樑。
以下是 re 模組中最常用的幾個函式:
-
re.search(pattern, string): 掃描整個字串,找到模式(pattern)第一次出現的位置,並回傳一個匹配物件(match object)。如果沒有找到匹配項,則回傳None。 -
re.match(pattern, string): 從字串的開頭開始匹配。如果字串開頭不符合模式,即使後面有匹配的部分,也會回傳None。 -
re.findall(pattern, string): 找到字串中所有(不重疊的)與模式匹配的子字串,並將它們作為一個列表回傳。 -
re.sub(pattern, repl, string): 替換(substitute)。在字串中找到所有與模式匹配的子字串,並用repl替換它們。回傳修改後的新字串。 -
re.compile(pattern): 將一個正則表達式模式編譯成一個正則表達式物件。如果一個模式需要被多次使用,預先編譯可以提高執行效率。
import re
text = "My phone number is 415-555-1234, and my office number is 415-555-9876."
# 1. re.search() - 尋找第一個電話號碼
pattern = r"\d{3}-\d{3}-\d{4}"
match = re.search(pattern, text)
if match:
print(f"Search found: {match.group(0)}") # .group(0) 回傳整個匹配的字串
# 2. re.findall() - 尋找所有電話號碼
all_numbers = re.findall(pattern, text)
print(f"Findall found: {all_numbers}")
# 3. re.sub() - 替換所有電話號碼
redacted_text = re.sub(pattern, "[REDACTED]", text)
print(f"Substituted text: {redacted_text}")
# 4. re.compile() - 編譯模式以提高效率
email_pattern = re.compile(r"(\w+)@(\w+\.\w+)")
email_text = "Contact me at alice@example.com or bob@work.net"
matches = email_pattern.findall(email_text)
print(f"Found emails: {matches}") # findall with groups returns a list of tuples
單元六:單元練習題
複習與應用
練習題 1:驗證電子郵件格式
編寫一個函式 `is_valid_email(email)`,使用正則表達式來判斷傳入的字串是否為一個有效的電子郵件格式。
- 一個有效的電子郵件應符合 `使用者名稱@網域名稱.頂級網域` 的基本形式。
- 使用者名稱可以包含字母、數字、底線、點和連字號。
- 網域名稱可以包含字母和數字。
- 頂級網域至少為兩個字母。
- 使用
re.match()來確保整個字串都符合模式。
練習題 2:提取所有網址
給定一段包含多個網址的文字,使用 re.findall() 提取出所有以 `http://` 或 `https://` 開頭的完整網址。
- 模式應能匹配 `http` 或 `https`。
- 網址的路徑可以包含字母、數字、斜線、點、連字號和底線。
- 考慮到網址可能結尾沒有斜線。
練習題 3:清理電話號碼格式
你有一批格式不統一的電話號碼資料,例如 `(02)-1234-5678` 或 `0912 345 678`。請使用 re.sub() 將這些號碼統一清理為 `0212345678` 或 `0912345678` 這種純數字的格式。
- 正則表達式需要找到所有非數字的字元。
- 使用 `re.sub()` 將所有匹配到的非數字字元替換為空字串 `''`。
單元七:模組化與套件管理
7.1 建立與使用模組
模組化是組織大型程式專案的基礎。在Python中,一個檔案就是一個模組,它允許我們將相關的函式、類別與變數組織在一起,形成一個獨立的命名空間。透過將程式碼分割到不同的模組中,我們可以提高程式的可讀性、可維護性,並促進程式碼的重用,避免將所有邏輯都混雜在單一檔案中。
要使用一個模組,我們需要使用 import 陳述句。
匯入模組的方式
import module_name: 匯入整個模組。使用時需要透過module_name.function_name的方式來存取。這是最推薦的方式,因為它能避免名稱衝突。from module_name import function_name, ClassName: 從模組中只匯入特定的函式或類別。使用時可以直接呼叫function_name。from module_name import *: 匯入模組中的所有名稱。強烈不建議這樣做,因為它會汙染當前的命名空間,容易導致名稱衝突且降低程式碼可讀性。import module_name as alias: 匯入模組並給它一個較短的別名,例如import pandas as pd。
💡 溫馨提醒
當你匯入一個模組時,Python 直譯器會執行該模組檔案中的所有頂層程式碼。這就是為什麼我們應該將可執行的腳本程式碼放在 if __name__ == "__main__": 區塊內的原因。
# --- 檔案 1: string_utils.py ---
"""這是一個處理字串的工具模組"""
PI = 3.14159
def reverse_string(s):
"""回傳反轉後的字串"""
return s[::-1]
class TextProcessor:
def __init__(self, text):
self.text = text
def count_words(self):
return len(self.text.split())
# --- 檔案 2: main.py ---
# 在同一個資料夾下建立 main.py
# 匯入整個模組 (推薦)
import string_utils
# 從模組中匯入特定成員
from string_utils import TextProcessor
# 使用模組中的成員
original = "hello world"
reversed_str = string_utils.reverse_string(original)
print(f"'{original}' 的反轉是 '{reversed_str}'")
# 使用匯入的類別
processor = TextProcessor("This is a sample sentence.")
word_count = processor.count_words()
print(f"句子中的單字數: {word_count}")
# 存取模組中的變數
print(f"模組中的 PI 值: {string_utils.PI}")
單元七:模組化與套件管理
7.2 套件管理工具 pip
在現代軟體開發中,我們很少從零開始,而是會大量使用由社群開發的第三方函式庫。套件管理器(Package Manager)就是用來自動化安裝、升級與管理這些外部函式庫(稱為套件)的工具。pip是Python的官方套件管理器,它連接到Python套件索引(PyPI),讓我們可以輕鬆地在專案中加入數以萬計的開源工具。
Pip 是一個命令列工具,以下是它最常用的一些指令:
-
pip install package_name: 安裝一個套件。例如:pip install requests。 -
pip install package_name==1.2.3: 安裝指定版本的套件。 -
pip install --upgrade package_name: 升級一個已安裝的套件到最新版本。 -
pip uninstall package_name: 解除安裝一個套件。 -
pip list: 列出當前環境中所有已安裝的套件及其版本。 -
pip show package_name: 顯示特定套件的詳細資訊,如版本、作者、授權等。 -
pip freeze > requirements.txt: 將當前環境中所有套件及其精確版本號碼匯出到一個名為requirements.txt的檔案中。這個檔案對於重現專案環境至關重要。 -
pip install -r requirements.txt: 從requirements.txt檔案中讀取並安裝所有列出的套件。這是在新環境中設定專案依賴的標準做法。
# 以下指令應在你的終端機或命令提示字元中執行
# 1. 安裝 requests 套件
pip install requests
# 2. 升級 requests 套件
pip install --upgrade requests
# 3. 安裝特定版本的 numpy
pip install numpy==1.21.0
# 4. 查看已安裝的套件
pip list
# 5. 產生 requirements.txt
pip freeze > requirements.txt
# 6. (在另一個乾淨的環境中) 根據 requirements.txt 安裝套件
pip install -r requirements.txt
# 7. 解除安裝套件
pip uninstall requests
單元七:模組化與套件管理
7.3 虛擬環境 venv
虛擬環境(Virtual Environment)是為了解決專案之間依賴衝突問題的關鍵工具。它允許我們為每個專案建立一個獨立、隔離的Python環境,每個環境都有自己的一組套件。這樣,專案A可以依賴某個套件的1.0版,而專案B可以同時依賴它的2.0版,兩者互不干擾。為每個專案使用虛擬環境是Python開發的標準最佳實踐。
從 Python 3.3 開始,venv 模組被內建到標準函式庫中,成為建立虛擬環境的標準方式。
使用 venv 的基本流程
-
建立虛擬環境:
在你的專案資料夾中,執行指令。這會建立一個名為
myenv的資料夾,其中包含了 Python 直譯器的副本和相關檔案。python -m venv myenv -
啟動虛擬環境:
你必須先「啟動」環境,才能在其中工作。啟動後,你的命令列提示符通常會顯示環境名稱。
- Windows:
myenv\Scripts\activate - macOS/Linux:
source myenv/bin/activate
- Windows:
-
在虛擬環境中工作:
啟動後,你使用
pip安裝的任何套件都只會被安裝到這個myenv環境中,而不會影響到全域環境。pip install numpy pandas matplotlib -
停用虛擬環境:
當你完成工作後,可以停用環境,回到系統的全域 Python 環境。
deactivate
💡 溫馨提醒
為每個專案建立一個獨立的虛擬環境是 Python 開發的最佳實踐。通常會將虛擬環境的資料夾名稱(如 myenv 或 .venv)加入到 .gitignore 檔案中,以避免將其提交到版本控制系統。
單元七:單元練習題
複習與應用
練習題 1:建立自訂數學模組
建立一個包含兩個檔案的專案,練習模組的建立與匯入。
- 建立一個名為 `my_calculator.py` 的模組,裡面包含 `add(x, y)` 和 `multiply(x, y)` 兩個函式。
- 建立一個主程式 `main.py`,從 `my_calculator` 模組中匯入這兩個函式。
- 在 `main.py` 中呼叫匯入的函式,並將結果印出。
練習題 2:管理專案依賴
假設你正在開發一個需要用到 `requests` 和 `matplotlib` 這兩個第三方套件的專案。請寫出對應的 `pip` 命令來完成以下操作。
- 安裝這兩個套件。
- 將目前環境中的所有套件依賴關係匯出到 `requirements.txt` 檔案中。
- (情境) 當你的同事拿到你的專案後,他該如何使用 `requirements.txt` 來安裝所有必要的套件?
練習題 3:建立專案虛擬環境
請寫出在命令列中,從頭開始建立一個名為 `data_analyzer` 的新專案,並為其設定虛擬環境的完整步驟。
- 建立一個名為 `data_analyzer` 的資料夾並進入。
- 在該資料夾內建立一個名為 `.venv` 的虛擬環境。
- 啟動這個虛擬環境(請同時提供 Windows 和 macOS/Linux 的指令)。
- 在虛擬環境中安裝 `pandas` 套件。
單元八:綜合練習
8.1 簡單練習題
練習題 1:字串數字加總器
編寫一個腳本,提示使用者輸入一串由逗號分隔的數字(例如 "10, 20, 3a, 40")。你的程式需要:
- 解析輸入的字串。
- 使用型別轉換 (casting) 將每個部分轉換為數字。
- 使用異常捕捉來處理任何非數字的輸入,忽略無效的部分並繼續處理有效的數字。
- 計算所有有效數字的總和。在迴圈內外部分別定義變數以觀察作用域 (scope)。
- 使用 f-string 格式化輸出來顯示總和以及被忽略的無效項目。
- 將核心邏輯放在
if __name__ == "__main__":區塊中。
練習題 2:簡易年齡驗證器
編寫一個腳本,詢問使用者的姓名和年齡。你的程式需要:
- 接收使用者輸入的姓名和年齡。
- 使用異常捕捉處理年齡不是有效數字的情況 (
ValueError)。如果輸入無效,應提示錯誤並結束程式。 - 使用型別轉換將有效的年齡輸入轉為整數。
- 定義一個全域變數
LEGAL_AGE = 18來展示作用域。 - 使用 f-string 和條件判斷,根據使用者年齡是否達到法定年齡,印出不同的問候語。
- 將核心邏輯放在
if __name__ == "__main__":區塊中。
練習題 3:安全的除法計算機
編寫一個腳本,執行安全的除法運算。你的程式需要:
- 提示使用者輸入被除數和除數。
- 使用異常捕捉同時處理兩種可能的錯誤:
ValueError(如果輸入不是數字)和ZeroDivisionError(如果除數為零)。 - 使用型別轉換將輸入轉為浮點數以便進行精確計算。
- 在
try...except...else...finally結構中:else區塊用於在計算成功時,使用 f-string 印出結果。finally區塊用於無論成功或失敗都印出一句「計算結束」。
- 定義一個變數
operation_count在主程式區塊中,以展示作用域。
單元八:綜合練習
8.2 中等練習題
練習題 1:JSON 資料處理與轉換
假設你有一個名為 students.json 的檔案,內容如下。編寫一個 Python 腳本來讀取此 JSON 檔案,計算每位學生的平均成績,並將結果(姓名、學號、平均成績)寫入一個新的 grades.csv 檔案中。
[
{
"student_id": "S001",
"name": "Alice",
"scores": [85, 92, 78]
},
{
"student_id": "S002",
"name": "Bob",
"scores": [90, 88, 94]
},
{
"student_id": "S003",
"name": "Charlie",
"scores": [65, 75, 70]
}
]
練習題 2:簡易通訊錄
使用物件導向程式設計,建立一個簡易的命令列通訊錄。你需要:
- 建立一個
Contact類別,包含name和phone屬性。 - 建立一個
ContactBook類別,內部用一個列表來儲存Contact物件。 ContactBook類別需要有以下方法:add_contact(contact): 新增聯絡人。display_all(): 顯示所有聯絡人。search_contact(name): 根據姓名搜尋聯絡人並回傳。save_to_file(filename): 將通訊錄儲存為 JSON 檔案。load_from_file(filename): 從 JSON 檔案載入通訊錄。
練習題 3:日誌檔案錯誤分析器
你正在分析一個伺服器日誌檔案 server.log。請編寫一個腳本,使用正則表達式來找出所有包含 "ERROR" 或 "FATAL" 的日誌行,並統計每種錯誤類型的數量。
[2023-10-27 10:00:00] INFO: User 'admin' logged in.
[2023-10-27 10:01:15] WARNING: Disk space is running low.
[2023-10-27 10:02:30] ERROR: Database connection failed.
[2023-10-27 10:03:00] INFO: Request processed successfully.
[2023-10-27 10:05:00] FATAL: System shutting down due to critical failure.
[2023-10-27 10:06:45] ERROR: Payment gateway timeout.
練習題 4:自訂模組與主程式
建立一個小專案,包含兩個檔案:
converters.py: 一個自訂模組,包含至少兩個函式:usd_to_twd(usd_amount): 將美金轉換為新台幣(假設匯率為 1:31.5)。celsius_to_fahrenheit(celsius): 將攝氏溫度轉換為華氏溫度。
main.py: 主程式檔案,它會匯入converters模組並使用其中的函式來執行轉換,然後印出結果。
單元八:綜合練習
8.3 困難練習題
練習題 1:迷你庫存管理系統
設計一個命令列介面的迷你庫存管理系統。這個專案需要綜合運用物件導向、檔案處理和資料結構。
- 物件導向設計:
- 建立一個基礎的
Product類別,包含product_id,name,price屬性。 - 使用繼承,建立至少兩個特定的產品子類別,例如
Book(額外有author屬性)和Electronics(額外有warranty_months屬性)。 - 建立一個
Inventory類別來管理所有產品。它應該使用一個字典來儲存產品,鍵是product_id,值是(product_object, quantity)的元組。
- 建立一個基礎的
- 功能需求:
Inventory類別應有方法來新增產品、更新庫存數量、查詢產品資訊。- 實現一個方法,可以生成一份庫存報告(純文字或 CSV 格式),列出所有產品、價格和庫存數量。
- 資料持久化(persistence):
- 實現
save_inventory和load_inventory方法,使用 JSON 格式來儲存和載入整個庫存狀態。你需要考慮如何序列化和反序列化不同類別的物件。
- 實現
練習題 2:遞迴檔案掃描與替換
編寫一個腳本,可以遞迴地掃描一個指定的資料夾及其所有子資料夾。這個腳本需要:
- 接受一個根目錄路徑、一個檔案副檔名(例如
.txt)、一個正則表達式模式和一個替換字串作為輸入。 - 使用
os.walk()來遍歷所有資料夾和檔案。 - 對於每個符合指定副檔名的檔案,讀取其內容。
- 使用
re.sub()來尋找並替換所有符合模式的內容。 - 將修改後的內容寫回原檔案。
- 將所有被修改的檔案路徑以及修改的次數記錄到一個
changelog.csv檔案中。
💡 溫馨提醒
這個操作會直接修改檔案,請務必在一個測試用的資料夾中執行,並備份好重要資料!
練習題 3:模組化的單位換算工具
建立一個具備單元測試的命令列單位換算工具。這個專案要求良好的結構和可靠性。
- 模組化設計: 建立一個名為
units的資料夾,使其成為一個套件(在資料夾中包含一個空的__init__.py檔案)。- 在
units內,建立length.py和temperature.py兩個模組。 length.py應包含 `meters_to_feet` 和 `feet_to_meters` 函式。temperature.py應包含 `celsius_to_fahrenheit` 和 `fahrenheit_to_celsius` 函式。
- 在
- 主程式: 建立一個主程式
main.py,它提供一個簡單的命令列介面,讓使用者選擇要進行的轉換類型,輸入數值,然後呼叫對應模組中的函式來顯示結果。主程式需要有良好的錯誤處理機制。 - 單元測試: 建立一個
tests資料夾。- 在
tests內,建立test_length.py和test_temperature.py。 - 使用
unittest模組為units套件中的所有轉換函式編寫測試案例,確保其計算的準確性。
- 在