Semorphe

函式與例外

定義函式,以及讓錯誤變成一條可以走的路。 · ⏱ 約 35 分鐘

你會學到三件事

  1. def:不用寫回傳型別,而可以有預設值
  2. lambda 和 *args
  3. try / except:錯誤不是當機,是一條分支

開始之前

C++ 的函式要寫回傳型別和每個參數的型別。 Python 兩個都不用,而換來的是你要自己知道。

🟢 先把編輯器打開,跟著下面一起做:跟著做

一、def

參數可以有預設值:

1def add(a, b=1):2    return a + b3 4print(add(3))         # 4   ← b 用預設值5print(add(3, 4))      # 7
變數a變數b+回傳3呼叫add34呼叫add印出印出定義函式add(參數a,b=1)1245

b=1 是預設值:呼叫時不給就用它。

⚠️ 有預設值的參數要放在後面。 def f(a=1, b) 是語法錯誤。

⚠️ 預設值不要用 list(def f(items=[]))。那個 list 是所有呼叫共用同一個, 而不是每次新的。這是 Python 最有名的一個坑,先知道有這件事就好。

二、*args:數量不定

前面加一個星號,它就收得下任意多個:

1def total(*args):2    return sum(args)3 4print(total(1, 2, 3))     # 6
變數args的總和回傳123呼叫total印出定義函式total(參數args)124

*args 把「傳進來的所有東西」收成一個 tuple。

積木盤上 sum 那一顆寫著「… 的總和」。

sum() 是內建的。Python 有很多這種內建函式(len max min sorted sum), 遇到「我想做某件很常見的事」時,先查一下有沒有。

三、lambda:一次性的小函式

只用一次的小函式可以不取名字:

1double = lambda x: x * 22print(double(5))          # 10
變數x2×臨時函式(參數x)算出5呼叫double印出設定double為12

它就是一個沒有名字的 def,而只能寫一個運算式。

積木盤上它寫著「臨時函式」——名字就是它的用途。

它的用處在「把一個函式當參數傳進去」:

🔎 這一段是試一下——打了看結果就好,不要留在程式裡。

1sorted(names, key=lambda s: len(s))     # 依長度排序
變數names變數s的長度臨時函式(參數s)算出排序,比較的依據,反過來1

⚠️ 不要用 lambda 取代 def。 上面那個 double = lambda x: ... 其實應該寫成 def double(x): return x * 2,一樣長,而好讀又好除錯。

四、try / except

積木上 try 那一顆寫著「嘗試 …」,另外兩格是 「沒出錯才 …」與「最後一定 …」。

接住的時候要說接住哪一種:

1try:2    n = int("abc")3except ValueError:4    print("轉換失敗")
"abc"把轉成整數設定n為"轉換失敗"印出遇到ValueError就把它叫做,然後嘗試沒出錯才最後一定124

int("abc") 轉不出來,於是它丟出一個 ValueError。

沒有 try 的話程式當場停掉;有的話,except 那一段接住它,程式繼續走。

意思
try: 這一段可能會出事
except ValueError: 出的是這種事的話,做這個
else: 都沒出事的話(少用)
finally: 不管有沒有出事都要做(關檔案之類)

⚠️ 不要寫光禿禿的 except:。 那會連你按 Ctrl+C 都接住, 而且會把你自己的打字錯誤一起吞掉。一個接住所有錯誤的程式, 與一個沒有錯誤的程式看起來一樣。

例外不是「錯誤處理」,是「另一條路」。 檔案不存在、使用者打錯字、網路斷了——這些本來就會發生, 它們不是 bug,是你的程式該想過的情況。

完成的樣子

這一課的參考做法——程式碼每一行的號碼,就印在對應的那塊積木上。

1def add(a, b=1):2    return a + b3 4def total(*args):5    return sum(args)6 7print(add(3))8print(add(3, 4))9print(total(1, 2, 3))10 11double = lambda x: x * 212print(double(5))13 14try:15    n = int("abc")16except ValueError:17    print("轉換失敗")
變數a變數b+回傳變數args的總和回傳3呼叫add34呼叫add123呼叫total變數x2×臨時函式(參數x)算出5呼叫double"abc"把轉成整數設定n為"轉換失敗"印出遇到ValueError就把它叫做,然後嘗試沒出錯才最後一定印出設定double為印出印出印出定義函式total(參數args)定義函式add(參數a,b=1)12457891112141517

換你了

寫一個函式,讀進使用者輸入並轉成整數。打錯字的話要求他重打,而不是當掉。

提示:while True: + try + 成功就 return。

到編輯器做這一題:練習:打錯就要求重打,而不是當掉

做一個

寫一個 safe_div(a, b):b 有預設值 1,而除以 0 的時候回傳「不能除以零」。 用 safe_div(10, 4)、safe_div(10)、safe_div(10, 0) 各叫一次,把結果印出來。

🔴 safe_div(10) 印出來是 10.0 不是 10,想一下為什麼。

到編輯器做這一題:做一個:不會當掉的除法

這一課你做了什麼

如果卡住了

你看到 多半是因為
SyntaxError: non-default argument follows default 有預設值的參數要放後面
例外沒被接住 except 的型別不對。先寫 except Exception as e: print(e) 看看是什麼
函式回傳 None 少了 return。Python 不寫 return 就回 None
預設值 list 每次呼叫都變長 那個經典的坑。預設寫 None,進函式再建新的
在編輯器打開這一課 →