這一週我總共寫了六篇文章:
第二十二天:讓時間動起來:Python 的 datetime 與 schedule 入門
第二十三天:用 Python 寫第九個小程式:打字遊戲
第二十四天:程式開發必備技能:Git & GitHub 的基礎應用
第二十五天:用 Python 寫第十個小程式:短網址生產器
第二十六天:Hash ≠ 加密,Base62 ≠ 魔法:資料處理入門基礎
第二十七天:用 Python 寫第十一個小程式:圖片壓縮工具
這六天的學習中,有三天偏重技術與觀念,另外三天則是小程式實作。
這週的學習內容橫跨時間控制、版本管理、資料編碼與多個小程式實作,讓我對「程式如何融入生活」有了更深的體會。透過 datetime 與 schedule,我學會讓程式與時間互動,實現自動提醒與排程的功能;在 Git 與 GitHub 的練習中,我初步掌握了版本控制的概念,理解團隊協作的基礎流程;而 Hash 與 Base62 的應用則讓我更明白資料處理的邏輯與轉換思維。除此之外,Typing Game、短網址工具與 PictureCompressor 這三個小程式的完成,讓我體會到從想法到作品的過程不僅是技術的累積,更是創意與實用性的結合,第四週的主題就像是在讓程式真正走入日常,從時間管理、資料壓縮到學習輔助,每個小功能都在提醒我——寫程式不只是解題,而是讓生活更有效率、更有趣的一種方式。
如果前三週的學習主題分別是「介面友善」、「專業化」與「程式走向世界」,那麼第四週我覺得核心就是「把程式能力落實到生活應用中」。透過小程式實作,我不僅練習了 Python 技巧,也開始理解如何把抽象概念變成實際可用的工具。因此,我想將我的小程式加上測試與錯誤處理功能,讓在預防程式崩潰的同時,也能在使用者犯錯時給出友善提示。
例如說這次我打算先讓我的短網址生產器加上測試與錯誤處理功能作為示範,接下來我們就一起來整理程式吧!
在這份程式中,錯誤處理主要透過 try-except 區塊來避免程式在發生例外狀況時直接崩潰,例如讀寫檔案、解析 JSON、或使用者輸入錯誤。
範例一:
def load_data():
global url_map
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
url_map = json.load(f)
except (json.JSONDecodeError, OSError):
print("⚠️ Error loading data, resetting file...")
url_map = {}
else:
url_map = {}
範例二在主程式中:
try:
choice = input("Select an option: ")
if choice == "1":
long_url = input("Enter URL: ").strip()
short = shorten_url(long_url)
print(f"✅ Shortened: {short}")
elif choice == "2":
short = input("Enter short code: ").strip()
print(f"🔗 Original: {get_original(short)}")
else:
print("❌ Invalid choice.")
except Exception as e:
print(f"⚠️ Unexpected error: {e}")
為了確保各功能模組能正確運作,加入了 unittest 測試架構。
這能讓我們自動化檢查「網址縮短」、「查詢原始網址」與「資料載入/儲存」是否都正常。
範例一:
import unittest
class TestURLShortener(unittest.TestCase):
def test_shorten_and_retrieve(self):
url = "https://example.com"
short = shorten_url(url)
self.assertEqual(get_original(short), url)
def test_nonexistent_code(self):
self.assertIsNone(get_original("xxxxxx"))
if __name__ == "__main__":
unittest.main()
這裡是加入測試與錯誤處理功能的短網址生產器程式碼,有興趣的人可以看[ Day25 ] 小小短碼,大大學習:用 Python 打造屬於自己的短網址工具作為參照比較。
這部分負責載入、儲存、維護短網址資料,url_data.json確保使用者關閉程式後資料仍能保存,並以錯誤處理機制防止檔案讀寫出錯。
DATA_FILE = "url_data.json"
url_map = {}
def load_data():
global url_map
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
url_map = json.load(f)
if not isinstance(url_map, dict):
url_map = {}
except (json.JSONDecodeError, OSError):
messagebox.showwarning("警告", "資料檔案損壞或格式錯誤,已重置。")
url_map = {}
else:
url_map = {}
def save_data():
try:
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(url_map, f, indent=4, ensure_ascii=False)
except Exception as e:
messagebox.showerror("錯誤", f"儲存失敗:{e}")
整個應用最重要的部分,負責產生、查詢、更新短網址邏輯,確保短碼唯一且統計次數正確。
def generate_short_code(length=6):
characters = string.ascii_letters + string.digits
return ''.join(random.choice(characters) for _ in range(length))
def shorten_url(long_url):
if not (long_url.startswith("http://") or long_url.startswith("https://")):
return None
short_code = generate_short_code()
while short_code in url_map:
short_code = generate_short_code()
url_map[short_code] = {"url": long_url, "count": 0}
save_data()
return f"http://short.ly/{short_code}"
def open_short_url(short_code):
if short_code in url_map:
url_map[short_code]["count"] += 1
save_data()
return url_map[short_code]["url"], url_map[short_code]["count"]
else:
return None, None
def generate_dummy_data(n=5, force=False):
if not force and url_map:
messagebox.showinfo("提示", "目前已有資料,若要強制新增請使用強制功能。")
return
sample_hosts = [
"https://example.com",
"https://github.com",
"https://docs.python.org",
"https://www.wikipedia.org",
"https://www.youtube.com"
]
added = 0
while added < n:
long_url = random.choice(sample_hosts)
short_code = generate_short_code()
if short_code in url_map:
continue
url_map[short_code] = {"url": long_url, "count": random.randint(0, 50)}
added += 1
save_data()
refresh_listbox()
messagebox.showinfo("完成", f"已新增 {added} 筆假資料。")
def run_tests():
url_map.clear()
save_data()
print("=== 測試開始 ===")
try:
url = "https://www.google.com"
short = shorten_url(url)
assert short and short.startswith("http://short.ly/")
print(f"生成短網址測試通過: {short}")
print("=== 測試結束,所有測試通過 ===")
messagebox.showinfo("測試完成", "所有測試已通過 (詳細請看控制台)")
except AssertionError:
messagebox.showerror("測試失敗", "有測試未通過,請查看控制台輸出。")
root = tk.Tk()
root.title("短網址管理器")
root.geometry("700x540")
root.configure(bg="#f0f4f7")
title_label = tk.Label(root, text="短網址管理器", font=("Microsoft YaHei", 18, "bold"), bg="#f0f4f7", fg="#333")
title_label.pack(pady=10)
listbox = tk.Listbox(root, width=95, height=16, font=("Consolas", 10))
listbox.pack(pady=5)
frame_buttons = tk.Frame(root, bg="#f0f4f7")
frame_buttons.pack(side=tk.BOTTOM, pady=15)
btn_generate = tk.Button(frame_buttons, text="生成短網址", command=generate_url, bg="#2196f3", fg="white", font=("Microsoft YaHei", 11, "bold"), width=15)
btn_generate.grid(row=0, column=0, padx=6)
btn_test = tk.Button(frame_buttons, text="執行測試", command=run_tests, bg="#9c27b0", fg="white", font=("Microsoft YaHei", 11, "bold"), width=15)
btn_test.grid(row=0, column=3, padx=6)
btn_dummy = tk.Button(frame_buttons, text="生成假資料(5筆)", command=lambda: generate_dummy_data(5, force=True), bg="#607d8b", fg="white", font=("Microsoft YaHei", 11, "bold"), width=18)
btn_dummy.grid(row=1, column=1, pady=10)
啟動程式時自動執行:1.載入資料。2.若資料為空,自動生成假資料。3.最後啟動主迴圈。
load_data()
# 若資料是空的,自動產生 5 筆假資料
if not url_map:
generate_dummy_data(5, force=True)
else:
refresh_listbox()
root.mainloop()
呈現五筆假資料來測試:
測試結果:
延續前幾天的實作,我今天想讓我的短網址工具更進一步——不只是能「動起來」,而是能「好好地動」。
於是,我加入了錯誤處理與測試程式的功能。
一開始看到那些 try-except、unittest 的語法時,其實有點怕會把整個程式搞亂。
但當我慢慢測試、觀察結果時,心裡有一種特別的成就感:
原本會當掉的地方,現在變成會彈出提示訊息;原本要一一點開檢查的功能,現在能靠測試自動驗證。
這次的學習讓我真的感受到,程式不只是冰冷的程式碼,而是一段段思考與溫度的累積。
我開始懂得如何讓自己的作品更穩定、更體貼,也更接近我理想中那個「能陪伴使用者」的智慧工具。
未來我會繼續挑戰更多進階應用,讓每一次的改版,都成為我前進的足跡~