Day 7 函式基礎
引言
到目前為止,我們寫的程式都是「一行一行往下執行」的直譯式結構。當同一段程式碼需要反覆出現,或是想把某個功能封裝起來重複使用時,這種寫法很快就會變得難以維護。函式(function)正是用來解決這個問題的工具,它讓我們可以把程式碼打包成可重複呼叫的單元。
這篇文章會帶你認識 Python 函式的基本概念,包括如何定義與呼叫函式、參數與回傳值的用法、預設參數與關鍵字參數的差異,以及如何為函式加上說明字串。學會函式後,你會發現程式碼可以寫得更短、更結構化,也更容易除錯。
定義與呼叫函式
在 Python 中,函式使用 def 關鍵字定義,後面接函式名稱、參數列與冒號,縮排內則是函式主體。定義好之後,必須「呼叫」函式才會真正執行。
def say_hello():
print("Hello, world!")
# 呼叫函式
say_hello() # 輸出 Hello, world!
say_hello() # 再呼叫一次
第一行 def say_hello(): 是函式的定義,本質上只是告訴 Python「有個叫做 say_hello 的函式,內容是這樣」,並不會立即執行。要讓函式真的做事,必須寫 say_hello() 來呼叫它。每次呼叫,函式內的程式碼都會重新執行一次,這就是函式可以「重複使用」的基礎。
參數:把資料傳進函式
函式最棒的地方在於可以接收外部傳進來的資料,稱為參數(parameter)。呼叫函式時傳入的實際資料則稱為引數(argument)。有了參數,同一段函式就能處理不同的輸入,達到真正的重複利用。
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 輸出 Hello, Alice!
greet("Bob") # 輸出 Hello, Bob!
這裡 name 是參數,在呼叫 greet("Alice") 時,「Alice」會被指定給 name,函式內就能用這個變數。函式可以定義多個參數,呼叫時依序傳入:
def add(a, b):
print(a + b)
add(3, 5) # 輸出 8
呼叫時傳入的引數數量必須與參數一致,否則會報 TypeError。這種「位置對應」的參數也稱為位置參數(positional argument)。
回傳值:把結果送回來
有些函式只是做事(例如 print()),但更多時候我們希望函式把計算結果「送回來」,這時候就要用 return 關鍵字。被 return 的值會成為函式的回傳值,呼叫端可以把它存進變數或繼續運算。
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 輸出 8
# 也可以直接在 print 中呼叫
print(add(10, 20)) # 輸出 30
return 同時也會結束函式的執行,函式內 return 之後的程式碼不會被執行。如果函式內沒有寫 return,則會自動回傳 None。
def no_return():
x = 1 + 1
print(no_return()) # 輸出 None
預設參數
有些參數在大部分情況下都會使用相同的值,這時可以給它一個預設值。呼叫函式時若沒有提供該參數,就會自動使用預設值;若提供了,則以提供的值為主。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # 輸出 Hello, Alice!
greet("Bob", "Hi") # 輸出 Hi, Bob!
greet("Carol", greeting="Hey") # 用關鍵字參數指定,輸出 Hey, Carol!
上面的 greeting="Hello" 就是預設參數。注意在函式定義中,有預設值的參數必須放在沒有預設值的參數之後,否則會直接報語法錯誤。這是因為 Python 在解析呼叫端的引數時,是依序對應的。
關鍵字參數
當函式有很多參數時,呼叫端記住每個參數的位置很麻煩,這時候可以用「關鍵字參數」,明確指定每個值對應到哪個參數名稱。
def introduce(name, age, city):
print(f"{name} 今年 {age} 歲,住在 {city}")
# 位置參數
introduce("Alice", 25, "Taipei")
# 關鍵字參數(順序可以不同)
introduce(city="Taipei", age=25, name="Alice")
兩種寫法的輸出完全相同。關鍵字參數讓程式碼更易讀,尤其當某些參數是布林值時,可讀性的差異更明顯(例如 savefig(filename, dpi=300, transparent=True))。位置參數與關鍵字參數也可以混用,但關鍵字參數必須寫在位置參數之後。
說明字串(docstring)
好的函式不只是能執行,還要讓別人(或未來的自己)看得懂。Python 慣例會在函式內第一行用三引號字串寫下「說明字串(docstring)」,說明函式的用途、參數與回傳值。
def celsius_to_fahrenheit(celsius):
"""將攝氏溫度轉換為華氏溫度。
Args:
celsius (float): 攝氏溫度。
Returns:
float: 對應的華氏溫度。
"""
return celsius * 9 / 5 + 32
print(celsius_to_fahrenheit(25)) # 輸出 77.0
print(celsius_to_fahrenheit.__doc__) # 顯示說明字串
說明字串可以用 help(函式名稱) 或 函式名稱.__doc__ 查看,是團隊協作與日後維護的重要工具。即使是自己寫的小工具,加上 docstring 也是一個好習慣。
函式的命名與撰寫慣例
Python 的函式命名遵循幾個簡單的慣例,掌握後能讓程式碼與社群一致,也更容易被其他開發者讀懂。
- 使用小寫與底線:函式名稱使用蛇形命名(snake_case),例如
calculate_total而不是CalculateTotal。這是 Python 官方風格指南 PEP 8 的建議。 - 動詞為主:因為函式代表「做一件事」,名稱通常以動詞開頭,例如
send_email、parse_config。回傳布林值的函式則常以is_、has_開頭,例如is_adult、has_permission。 - 一個函式做一件事:盡量讓函式只負責單一功能,這樣更容易理解與測試。如果一個函式太長或做太多事,就應該考慮拆成幾個小函式。
舉個簡單的例子,下面的 is_even() 就是個命名良好的函式:
def is_even(number):
"""判斷 number 是否為偶數。"""
return number % 2 == 0
print(is_even(4)) # True
print(is_even(7)) # False
變數的作用域
函式內定義的變數,預設只在函式內可見,這個範圍稱為「區域作用域(local scope)」。函式外看不到函式內的變數,反之亦然。這種設計避免了不同函式之間不小心互相干擾,是寫大型程式的重要基礎。
def add(a, b):
total = a + b # total 是區域變數
return total
result = add(3, 5)
print(result) # 輸出 8
# print(total) # 這行會報 NameError,因為 total 不存在於全域
如果想在函式內讀取(或修改)函式外的全域變數,可以用 global 關鍵字宣告。不過多數情況下,更推薦的做法是「把值當參數傳進去、把結果用 return 傳出來」,而不是直接動全域變數,這樣程式碼會更容易追蹤與測試。
把函式組合成模組化程式
學會函式之後,我們可以把一個較大的問題拆成幾個小函式,每個函式負責一部分工作,最後再組合成完整的程式。舉例來說,計算一份成績報告,可以拆成「算平均」、「算最高分」、「算最低分」、「輸出報表」等小函式。
def average(scores):
return sum(scores) / len(scores)
def top_score(scores):
return max(scores)
def low_score(scores):
return min(scores)
def report(name, scores):
print(f"{name} 的成績報告")
print(f" 平均:{average(scores):.1f}")
print(f" 最高:{top_score(scores)}")
print(f" 最低:{low_score(scores)}")
report("Alice", [85, 92, 78, 88])
這種「把大問題拆成小函式」的做法,不只讓程式碼更易讀,也方便日後維護與測試。例如當「平均的計算方式」要改成加權平均時,只需要修改 average() 一個函式,其他地方都不用動。這也是寫程式時非常重要的「單一職責原則」精神。
常見錯誤與小結
初學者寫函式時,有幾個常見的錯誤要特別注意:忘記 return、參數與引數數量不符、忘記冒號或縮排錯誤、把可變物件(串列、字典)當預設值等。養成「寫完函式先手動測試幾組輸入」的習慣,是避免這些錯誤的最好方法。
函式是把「會重複用到的邏輯」打包成可重複呼叫單元的核心工具。今天我們從最基本的 def 開始,一路學到參數、回傳值、預設值、關鍵字參數、說明字串與作用域。掌握這些之後,你已經具備寫出結構化、可維護程式碼的能力。
結語
今天我們認識了 Python 函式的基礎:使用 def 定義函式、用 () 呼叫函式、用參數接收輸入、用 return 回傳結果。我們也學會了預設參數、關鍵字參數、說明字串與變數作用域的概念。掌握函式之後,你已經能把重複的程式碼打包成可重複使用的單元,程式結構也會更清楚。
明天,我們會更進一步探討函式的進階主題,包括可以接收任意數量引數的 *args 與 **kwargs、更詳細的作用域規則、匿名函式 lambda,以及用遞迴解決問題的思路。
留言
張貼留言