2026 Apr. 19 講師 藍少君
「程式碼被閱讀的次數,遠多於被撰寫的次數」
很糟的 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! 作業的時候,發現大家不太會排版 QQ
適當使用空格(在運算符號前後),讓程式看起來不會這麼緊湊。
a = 1 b = a * a
a=1 b=a*a
適時的加入空行,分開程式碼的不同段落。
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()
可以從視覺上知道,這兩段程式碼可以分開理解、邏輯獨立。
一律使用四個空格作為縮排。
def foo(): print("foo") # 4 spacs, good
def foo(): print("foo") # 8 spaces, bad def foo(): print("foo") # 2 spaces, bad
不建議將多行語句寫在同一行。
while one == 1: # good print("Hello") one = 0
while one == 1: print("Hello"); one = 0 # bad
=
+=
-=
*=
>
<
>=
<=
==
is
in
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])
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
class Pokemon: def __self__(self, name, type_: str, owner: str): self.name = name self.type = type_ self._owner = owner
更具體的命名策略(reference: 前年講師的簡報):
is_xxx
def is_done() -> bool: return done_flag == 1 def get_container(obj): return obj.parent
善用 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") ]
在不符合條件時,採用提早退出的策略,以減少不必要的縮排。
不推薦寫法:
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
善用表格來簡化類似的邏輯判斷。
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]])
執行以下程式會發生什麼事?
n = 30 arr = [1, 2, 3] for i in range(n): print(arr[i], end="-")
會得到 IndexError: list index out of range。
IndexError: list index out of range
我們會說,這支程式有 bug
那執行以下程式呢?
n = int(input()) arr = input().split() for i in range(n): print(arr[i], end="-")
如果輸入是:
3 1 2 3
會得到 1-2-3。
1-2-3
4 1 2 3
n
事件報告: https://blog.cloudflare.com/zh-tw/18-november-2025-outage/
圖片來源: https://learn.microsoft.com/en-au/answers/questions/1116917/process-explorer-keeps-on-crashing-with-memory-add
由 Microsoft - 自己的作品, 公有領域, https://commons.wikimedia.org/w/index.php?curid=184656745
By Nerdopolis - Own work, CC BY-SA 4.0,https://commons.wikimedia.org/w/index.php?curid=159322035
error 指的是,我們預期程式會進行某些行為,但這些行為因某些不可控因素,未被滿足,引發非預期行為。
通常 bug 指的是,程式邏輯本身有錯,造成程式於執行時做出不合法行為或非預期行為,導致程式崩潰或表現未如預期。
不用特別區分這兩者,很多時候 bug 和 error 是一體的。 我們不應該讓程式出現 bug,而針對潛在的 error,我們也要提前防禦。
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="-")
在做 File I/O 時,會遇到什麼 error 呢?
在某些語言的習俗中,功能的提供方,會將 error 一起放在功能回覆的欄位,來告訴使用方,這是成功還是發生錯誤。
def connect(): """ some code to create a connection """ if fail: return None return conn
在部份 python 套件中,你仍然會看到這種 style 的 error 回傳方式。
將 error(exception) 放進獨立的訊息通道裡傳遞。
python 是實做這個 model。
只有想要處理 exception 的人,才需要把它拿起來看,進行處理。
如果沒有人處理(包括你的 code),這個 exception 會一路衝到最頂端,最後導致程式直接崩潰。不會有默默地用錯誤回傳值,執行業務邏輯的情況。
try 建立了會被監控的範圍,except 會接這個範圍內發生的 exception (可指定),執行指定程式進行處理。接起後 exception 就被處理掉了。
try
except
a, b = map(int, input().split()) try: print(f"a/b: {a/b}") except ZeroDivisionError: print("b should not be 0")
沒有 exception 發生:
1 2
a/b: 0.5
除以 0 的 exception 發生:
1 0
b should not be 0
a, b = map(int, input().split()) print(f"a/b: {a/b}")
ZeroDivisionError: division by zero
並且注意到,程式的結束狀態碼不為 0,也就是程式被標記為異常結束。
可以 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.")
一次接起所有 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
IndexError: list assignment index out of range
自訂 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'>
如果沒有任何 exception 被接起來,則會執行 else block 的內容。
else
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 block,在離開前一定會執行 finally 的 block,無論如何!
finally
def foo(): try: a = 1 / 0 except ZeroDivisionError: return finally: print("called") foo()
圖片來源: https://realpython.com/python-exceptions/
一個可以讓 terminal 輸出看起來超酷的工具!
rich/rich/__init__.py:
rich/rich/__init__.py
try: _IMPORT_CWD = os.path.abspath(os.getcwd()) except FileNotFoundError: # Can happen if the cwd has been deleted _IMPORT_CWD = ""
用 Raspberry Pi、觸碰感測器、音響作為硬體,寫了一個 python script,製作碰到歌手相片會播放音樂的應用。
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()
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)
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()
try / except / else / finally
try / except