Semorphe

函式

給一段程式取個名字,之後就叫得動它。 · ⏱ 約 30 分鐘

你會學到三件事

  1. def 定義一個函式
  2. 參數進去、回傳值出來
  3. 函式裡面的變數只活在裡面

開始之前

第 2 課你給一個值取了名字。這一課要給一段程式取名字。

而你其實一直在用別人寫好的:print、len、input、int 都是函式。

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

一、def

照著打上這一段:

1def add(a, b):2    return a + b3 4print(add(3, 4))      # 7
變數a變數b+回傳34呼叫add印出定義函式add(參數a,b)124
def add(a, b):
 ↑   ↑   ↑    ↑
關鍵字 名字 參數  冒號(和 if / for 一樣)
    return a + b
    └─ 縮排 = 函式的身體

⚠️ 函式要先定義才能用,定義寫在呼叫的後面會 NameError。

積木盤上這一顆寫著「定義函式 …」。

二、return

return 同時做兩件事:

  1. 把值交出去
  2. 立刻結束這個函式,後面的行不會跑

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

1def f(n):2    return n3    print("印不出來")      # ← 永遠不會跑
變數n"印不出來"印出回傳定義函式f(參數n)123

沒有 return 的函式回傳 None:

1def greet(name):2    print("你好," + name)3 4x = greet("小明")5print(x)              # None
"你好,"變數name+印出"小明"呼叫greet變數x印出設定x為定義函式greet(參數name)1245

⚠️ 這是一個常見的困惑:「印出來」和「交出去」是兩件不同的事。

給誰
print(x) 給人看
return x 給呼叫它的那段程式用

三、參數可以有預設值

參數後面加上 =,不給的時候就用那個值:

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

1def greet(name, greeting="你好"):2    print(greeting + "," + name)3 4greet("小明")             # 你好,小明5greet("小明", "早安")      # 早安,小明
變數greeting","+變數name+印出"小明""小明""早安"呼叫greet呼叫greet定義函式greet(參數name,greeting="你好")1245

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

四、🔴 裡面的變數只活在裡面

函式裡面取的名字,外面看不到:

🚫 這一段是反例——課文在說不要這樣寫,別照著拉。

1def f():2    x = 103    print(x)4 5f()6print(x)          # 💥 NameError:外面沒有 x
10變數x印出設定x為變數x印出呼叫f定義函式f12536

函式裡宣告的變數叫區域變數,函式結束它就消失了。

好處是:你在函式裡取什麼名字,都不會撞到外面的東西。

⚠️ 反過來,函式讀得到外面的變數,而改不到:

🚫 這一段是反例——課文在說不要這樣寫,別照著拉。

1total = 02def add():3    total = total + 1      # 💥 UnboundLocalError
0變數total1+設定total為定義函式add設定total為123

要改外面的東西,正確的做法是回傳它:

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

1def add(total):2    return total + 13 4total = add(total)
變數total1+回傳變數total呼叫add設定total為定義函式add(參數total)124

(有個 global 關鍵字可以強行改,而幾乎永遠不該用它。)

五、為什麼要寫函式

① 同一段程式要用好幾次      → 不用複製貼上
② 一段程式做的事需要一個名字 → 讀的人不必看細節
③ 可以單獨測試那一小段      → 出錯時範圍小

函式不是「進階語法」——它是你第一次覺得 「這幾行我不想再寫一遍」的時候,自然會想要的東西。

完成的樣子

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

1def add(a, b):2    return a + b3 4def greet(name):5    print("你好," + name)6 7print(add(3, 4))8greet("小明")
變數a變數b+回傳"你好,"變數name+印出34呼叫add"小明"呼叫greet印出定義函式greet(參數name)定義函式add(參數a,b)124578

把積木打散,自己排回去:排回去:把剛才那支程式的積木排好

換你了

寫一個 is_leap(year),回傳 True 或 False(第 5 課那個閏年規則)。

⚠️ 要 return,不要 print,這樣它才用得進 if is_leap(2000):。

到編輯器做這一題:練習:寫一個 is_leap(year),要 return 不要 print

做一個

寫兩個函式算最大公因數:一個用迴圈,一個叫自己(遞迴)。兩個答案都印出來。

輾轉相除法只有一句話:gcd(a, b) = gcd(b, a % b),而 b 變成 0 的時候答案就是 a。 ⚠️ 裁判用的是 48 和 18。哪一個版本比較好讀?跟旁邊的人不一定同答案。

到編輯器做這一題:做一個:最大公因數,兩種寫法

這一課你做了什麼

如果卡住了

你看到 多半是因為
NameError: add 函式定義寫在呼叫的後面了
函式回傳 None 少了 return
UnboundLocalError 在函式裡改了外面的變數。改成回傳它
TypeError: missing argument 呼叫時參數個數不對
函式印出來了卻不能拿來用 print 不是 return
在編輯器打開這一課 →