Coding Style & Error Handling

2026 資訊之芽 Python 語法班 簡報

2026 Apr. 19 講師 藍少君

Slido

slido qrcode center

#sproutpy2026

Coding Style

如何寫好看的程式

「程式碼被閱讀的次數,遠多於被撰寫的次數」

錯誤示範

很糟的 coding style 會帶來的問題:

v=int(input());V=[0]*v;A=[[0]*3for _ in range(v)]
for _ in range(v):x,a,b,c=map(int,input().split());A[x][0]=a;A[x][1]=b;A[x][2]=c
def t(n):V[n]=1;[t(A[n][i])for i in range(3)if not V[A[n][i]]]
t(0)
print("True"if sum(V)==v else"False")

如何改進

基礎排版 (1)

我在看大家 1! 作業的時候,發現大家不太會排版 QQ

適當使用空格(在運算符號前後),讓程式看起來不會這麼緊湊。

a = 1
b = a * a
a=1
b=a*a

基礎排版 (2)

適時的加入空行,分開程式碼的不同段落。

n, m = map(int, input().split())
mat = [list(map(int, input().split())) for _ in range(n)]

for i in range(n):
    for j in range(m):
        print(mat[i][j], end=" ")
    print()

可以從視覺上知道,這兩段程式碼可以分開理解、邏輯獨立。

PEP8 是什麼?

  • 針對 coding style,大方向是希望程式碼清晰易讀。
  • 除此之外,某些固定的寫法,能夠給閱讀者更多資訊,理解這段程式。
  • 我們要避免大家都有共識很糟的寫法,也要維持一致的習慣,沒有絕對的對錯,僅是希望同一份專案的程式碼能有一致的程式風格。
  • 在一些大型專案中,針對 coding style 會定義自己的習俗,一般來說我們要入境隨俗。
  • 針對 python 的程式,python 官方有希望大家遵守的習俗,放在 PEP8 這份提案文件裡面。

PEP8 推薦排版 (1)

一律使用四個空格作為縮排。

def foo():
    print("foo") # 4 spacs, good
def foo():
        print("foo") # 8 spaces, bad

def foo():
  print("foo") # 2 spaces, bad

PEP8 推薦排版 (2)

不建議將多行語句寫在同一行。

while one == 1: # good
    print("Hello")
    one = 0
while one == 1: print("Hello"); one = 0 # bad

PEP8 推薦排版 (3)

  • 在 assignment (=)、augmented assignments (+=、-=、*=)、comparisons (>、<、>=、<=、==、is、in)、boolean operations (and、or、not) 的周圍加上空白。
  • 在四則運算的式子中,在優先級最低的運算符周圍加上空白。
a = 1
b += 1
if b > a and c == 1:
    c = a*b + d

變數命名

使用有意義的變數名稱,能有效幫助閱讀程式碼的人理解程式。

錯誤示範:

a = "Alice"
b = {
    "Alice": ["banana", "apple"],
    "Bob": ["juice"]
}
print(b[a])

正確示範:

username = "Alice"
shopping_carts = {
    "Alice": ["banana", "apple"],
    "Bob": ["juice"]
}
print(shopping_carts[username])

PEP8 推薦變數命名 (1)

  • 常數(不想變動的固定值)全大寫配合底線。
  • 變數、函數、檔名用底線。
  • 類別名用駝峰。
MAX_NR_PEOPLE = 4096

current_state = IDLE

rows, cols = map(int, input().split())
def get_index(arr: list[int], key: int) -> int:
    pass
class SuperMario:
    def __init__(self):
        pass

PEP8 推薦變數命名 (2)

  • 避免關鍵字衝突,可以在後面補一個底線。
  • 表示內部函式而非 API,可以在前面補一個底線。
class Pokemon:
    def __self__(self, name, type_: str, owner: str):
        self.name = name
        self.type = type_
        self._owner = owner

其他推薦變數命名

更具體的命名策略(reference: 前年講師的簡報):

  • 用 is_xxx 來命名布林值/回傳布林值的函式。
  • 用動詞來為 function 命名。
def is_done() -> bool:
    return done_flag == 1

def get_container(obj):
    return obj.parent

簡化程式 (1)

善用 function、class 將程式模組化,避免撰寫冗餘的程式。

print("[Error] You should not reach this line")
print("[Error] You accessed private resource")
def print_error(*args, **kargs):
    print("[Error]", *args, **kargs)

print_error("You should not reach this line")
print_error("You accessed private resource")
people_id = [3, 5, 2]
people_name = ["Jane", "Alice", "Kimu"]
class Person:
    def __init__(self, id_, name):
        self.id = id_
        self.name = name

people = [
    Person(3, "Jane"), Person(5, "Alice"), Person(2, "Kimu")
]

簡化程式 (2)

在不符合條件時,採用提早退出的策略,以減少不必要的縮排。

不推薦寫法:

def foo():
    if is_valid():
        print("foo")
        x, y = 1, 1
        return 0
    else:
        return 1

推薦寫法:

def foo():
    if not is_valid():
        return 1
    print("foo")
    x, y = 1, 1
    return 0

簡化程式 (3)

善用表格來簡化類似的邏輯判斷。

不推薦寫法:

if month == 1:
    print("31 days")
elif month == 2:
    print("28 days")
elif month == 3:
    print("31 days")
...

推薦寫法:

month_to_days = [0, 31, 28, 31, ...]
print(f"{month_to_days[month]} days")

不推薦寫法:

print(matrix[y+1][x])
print(matrix[y][x+1])
print(matrix[y-1][x])
print(matrix[y][x-1])

推薦寫法:

dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]
for dir in range(4):
    print(matrix[y+dy[dir]][x+dx[dir]])

喘口氣~

以上是 coding style 的部份

Error Handling

如何寫好穩的程式

Runtime Error Example

執行以下程式會發生什麼事?

n = 30
arr = [1, 2, 3]

for i in range(n):
    print(arr[i], end="-")

會得到 IndexError: list index out of range。

我們會說,這支程式有 bug

"Implicit" Runtime Error Example

那執行以下程式呢?

n = int(input())
arr = input().split()

for i in range(n):
    print(arr[i], end="-")

如果輸入是:

3
1 2 3

會得到 1-2-3。

如果輸入是:

4
1 2 3

會得到 IndexError: list index out of range。

什麼是 Error

  • 對於第二個程式,我們預設使用者輸入的 n 為第二行數字的個數。
  • 若使用者 不知道/不小心/故意 沒有遵守這個規則,則程式會非預期的崩潰。
  • 對於這樣的非預期行為,我們稱為 error。
  • 對於開發中的程式,產生 error 我們可以找到 error 來源,並嘗試解決或選擇忽略。
  • 對於完整的程式應用,我們不希望出現 error。即使出現 error,我們想要體面的結束程式,而不是留下一堆爛攤子。

Case Study: Cloudflare Outage (2025)

  • Cloudflare
    • 全球網路基礎建設公司。
    • 提供 CDN、網站防禦、防 DDoS 攻擊等服務。
  • 2025/11/18 Cloudflare 崩潰事件
    • 起因來自更新的設定檔內容異常,造成沒有做 error handling 的程式崩潰。
    • 造成全球性網路服務下線事件。
    • 包含 ChatGPT、Canva、Spotify 等耳熟能詳的應用,有使用 Cloudflare 服務的應用皆受影響。

事件報告: https://blog.cloudflare.com/zh-tw/18-november-2025-outage/

Process Crash Example

explorer crash center

圖片來源: https://learn.microsoft.com/en-au/answers/questions/1116917/process-explorer-keeps-on-crashing-with-memory-add

Windows BSOD(Blue Screen of Death)

Windows BSOD center

由 Microsoft - 自己的作品, 公有領域, https://commons.wikimedia.org/w/index.php?curid=184656745

Linux Kernel Panic

kernel panic center

By Nerdopolis - Own work, CC BY-SA 4.0,https://commons.wikimedia.org/w/index.php?curid=159322035

更多 Error 說明

error 指的是,我們預期程式會進行某些行為,但這些行為因某些不可控因素,未被滿足,引發非預期行為。

  • 輸入未符合格式
  • 記憶體/硬碟空間不足
  • 權限不足
  • 沒有網際網路連線
  • ...

Error vs. Bug

通常 bug 指的是,程式邏輯本身有錯,造成程式於執行時做出不合法行為或非預期行為,導致程式崩潰或表現未如預期。

  • 除以 0
  • Null Pointer Access
  • ...

不用特別區分這兩者,很多時候 bug 和 error 是一體的。
我們不應該讓程式出現 bug,而針對潛在的 error,我們也要提前防禦。

簡單 Error Handling 做法

import sys

n = int(input())
arr = input().split()

if len(arr) != n:
    print(f"[Error] Expected {n} elements, but got {len(arr)}.")
    sys.exit(1)

for i in range(n):
    print(arr[i], end="-")

Error Handling in File I/O

在做 File I/O 時,會遇到什麼 error 呢?

  • 開啟一個不存在的檔案
  • 有一個同名的資料夾檔案
  • 沒有寫入權限,但嘗試以寫入模式開啟
  • 硬碟空間不足,無法寫入
  • ...

為什麼要學 Error Handling

  • 在實務的應用(也就是 2! 會教大家的)中,我們的程式常常會和底層系統、其他人寫好的程式庫 打交道。
  • 別人的程式通常是提供功能,而我們的程式通常是使用別人的功能,完成某項應用任務。
  • 功能的提供方,通常會將內部發生的錯誤,回報給功能的使用方,由使用方自行決定如何處理。
  • 我們要學習,收到 error 回報的方式,以及處理 error 的方法。

不同的 Error 回報機制

在某些語言的習俗中,功能的提供方,會將 error 一起放在功能回覆的欄位,來告訴使用方,這是成功還是發生錯誤。

def connect():
    """
    some code to create a connection
    """
    if fail:
        return None
    return conn

在部份 python 套件中,你仍然會看到這種 style 的 error 回傳方式。

Error Handling Model - Return Value

  • 所有人路徑上的 code 都必須意識到這個 error 的存在。
  • 你一定得知道有這個 error 的存在,不然程式默默的用錯的結果繼續執行,造成更災難的後果。

mode 1 center

Error Handling Model - Exception Channel

將 error(exception) 放進獨立的訊息通道裡傳遞。

model 2 center

python 是實做這個 model。

Error Handling Model - Exception Channel

只有想要處理 exception 的人,才需要把它拿起來看,進行處理。

model 2 center

Error Handling Model - Exception Channel

如果沒有人處理(包括你的 code),這個 exception 會一路衝到最頂端,最後導致程式直接崩潰。不會有默默地用錯誤回傳值,執行業務邏輯的情況。

model 2 center

try / except 語法 (1)

try 建立了會被監控的範圍,except 會接這個範圍內發生的 exception (可指定),執行指定程式進行處理。接起後 exception 就被處理掉了。

a, b = map(int, input().split())
try:
    print(f"a/b: {a/b}")
except ZeroDivisionError:
    print("b should not be 0")

沒有 exception 發生:

  • input: 1 2
  • output: a/b: 0.5

除以 0 的 exception 發生:

  • input: 1 0
  • output: b should not be 0

不做 error handling

a, b = map(int, input().split())
print(f"a/b: {a/b}")

沒有 exception 發生:

  • input: 1 2
  • output: a/b: 0.5

除以 0 的 exception 發生:

  • input: 1 0
  • output: ZeroDivisionError: division by zero

並且注意到,程式的結束狀態碼不為 0,也就是程式被標記為異常結束。

try / except 語法 (2)

可以 except 多種不同的 exception。

try:
    x = int(input("Enter a number: "))
    result = 10 / x
except ZeroDivisionError:
    print("You cannot divide by zero.")
except ValueError:
    print("Invalid input. Please enter a valid number.")

try / except 語法 (3)

一次接起所有 exception (built-in, non-system-exiting exceptions)。

常見於 server 等應用,在遇到輕微 exception 時,單純留下紀錄就好。對於嚴重性高的(如從外部強行終止程式的事件),就讓他 crash 沒關係。

try:
    pass
except Exception as e:
    print(e)       # error message
    print(type(e)) # error type

舉例:

try:
    arr = []
    arr[0] = 1
except Exception as e:
    print(e)       # error message
    print(type(e)) # error type
list assignment index out of range
<class 'IndexError'>

也就是大家常看到的 IndexError: list assignment index out of range

try / except 語法 (4)

自訂 exception 和自己發起 exception。在套件 source code 裡會看到。

class MyError(Exception):
    pass

try:
    raise MyError("HaHa")
except MyError as e:
    print(e)       # HaHa
    print(type(e)) # <class '__main__.MyError'>

try / except / else 語法

如果沒有任何 exception 被接起來,則會執行 else block 的內容。

def foo(a, b):
    try:
        c = a / b
    except ZeroDivisionError:
        print("b should not be zero")
    else:
        print(f"a/b: {c}")

foo(1, 2) # a/b: 0.5

try / except / finally 語法

強力的善後語法,只要進入了 try block,在離開前一定會執行 finally 的 block,無論如何!

def foo():
    try:
        a = 1 / 0
    except ZeroDivisionError:
        return
    finally:
        print("called")
foo()

try / except / else / finally 執行順序

teef_order center

圖片來源: https://realpython.com/python-exceptions/

Case Study: rich

一個可以讓 terminal 輸出看起來超酷的工具!

rich/rich/__init__.py:

try:
    _IMPORT_CWD = os.path.abspath(os.getcwd())
except FileNotFoundError:
    # Can happen if the cwd has been deleted
    _IMPORT_CWD = ""

Case Study: Maloney's Music Player

用 Raspberry Pi、觸碰感測器、音響作為硬體,寫了一個 python script,製作碰到歌手相片會播放音樂的應用。

rbp center

code (疑似是 python 2):

try:
    import RPi.GPIO as GPIO
    import os, time
    GPIO.setmode(GPIO.BOARD)
    ...
except KeyboardInterrupt:
        os.system('mpc -q stop')
        os.system('mpc -q volume 100')
        print "Program terminated."
finally:
        GPIO.cleanup()

Case Study: pooiscoding's Judge Script

try:
    for i in range(NR_SUBSCRIBERS):
        subscribers[i] = run_command(...)
        if not wait_for_output(...):
            kill_all_procs()
            sys.exit(JUDGE_WA)
    ...
    kill_all_procs()
except Exception as e:
    kill_all_procs()
    sys.exit(JUDGE_FATAL)

Refactor Using finally

try:
    for i in range(NR_SUBSCRIBERS):
        subscribers[i] = run_command(...)
        if not wait_for_output(...):
            sys.exit(JUDGE_WA)
    ...
except Exception as e:
    sys.exit(JUDGE_FATAL)
finally:
    kill_all_procs()

Takeaways

  • Coding Style
    • 良好的 coding style (排版、命名) 可以大幅提升程式可讀性。
    • 跟著簡報的內容做,練習撰寫專業、簡潔、美觀的 python 程式碼。
  • Error Handling
    • 理解 error handling 的重要性,與和 2! 課程的關聯性。
    • 理解 return value model 和 exception model 的區別。
    • python 的 try / except / else / finally 語法。
    • try / except 的實務使用情境。

迷你作業

  • 以 File I/O 為題,幫一支寫好的 Python 程式做 Error Handling。
  • 讓大家開始練習 2! 的各種必備技能。
  • spec 連結
  • 作業繳交表單
  • 繳交期限: 2026/05/04 00:00

End