本文 AI 產出,尚未審核

Python

單元:函式(Functions)

主題:函式宣告與呼叫


簡介

在程式設計的世界裡,**函式(function)**是將一段具體的工作抽象化、模組化的關鍵工具。透過函式,我們可以把重複的程式碼集中管理、提升可讀性、降低錯誤率,甚至讓程式的測試與維護變得更輕鬆。

對於 Python 初學者而言,掌握 函式的宣告與呼叫 是邁向「寫得好、寫得快」的第一步;對於已具備基礎的開發者,深入了解參數傳遞機制、預設值與可變參數,則是撰寫彈性、可擴充程式的必要功力。本文將以 實務導向 的方式,從最基本的函式定義說起,逐步帶入常見的技巧與陷阱,讓讀者在閱讀完後能立即在自己的程式中運用。


核心概念

1. 基本函式宣告與呼叫

在 Python 中,使用關鍵字 def 來宣告函式,語法如下:

def 函式名稱(參數1, 參數2, ...):
    """可選的說明文件 (docstring)"""
    # 函式本體
    執行的程式碼
    return 回傳值   # 若未寫 return,預設回傳 None

範例 1:最簡單的 Hello World 函式

def say_hello():
    """印出歡迎訊息"""
    print("Hello, Python!")

# 呼叫函式
say_hello()

重點:函式名稱遵循變數命名規則,且 冒號 (:) 為必寫,縮排代表函式的程式區塊。


2. 參數與回傳值

函式可以接受 位置參數(positional arguments),並透過 return 把結果回傳給呼叫端。

範例 2:計算兩數相加

def add(a, b):
    """回傳 a 與 b 的和"""
    result = a + b
    return result

sum_ = add(3, 5)          # 呼叫時傳入 3 與 5
print("3 + 5 =", sum_)   # 輸出: 3 + 5 = 8
  • ab形參(formal parameter),在呼叫時傳入的實際值稱為實參(actual argument)。
  • return 後的表達式會立即結束函式,並把值交給呼叫處;若省略 return,函式預設回傳 None

3. 預設參數與關鍵字參數

為了提升函式的彈性,Python 允許在宣告時為參數設定預設值,呼叫時可以省略該參數;同時也支援關鍵字參數(keyword arguments),讓參數的順序不再重要。

範例 3:帶預設值的函式

def greet(name, greeting="哈囉"):
    """根據提供的名字與問候語產生訊息"""
    print(f"{greeting},{name}!")

greet("小明")                     # 使用預設的 greeting,輸出: 哈囉,小明!
greet("阿美", greeting="早安")   # 使用關鍵字參數,輸出: 早安,阿美!
greet(greeting="晚安", name="大雄") # 參數順序任意,輸出: 晚安,大雄!

技巧:預設參數必須放在 非預設參數之後,否則會產生 SyntaxError


4. 可變參數(*args, **kwargs)

有時候我們無法預先知道會傳入多少參數,這時可使用 *args(收集位置參數)與 **kwargs(收集關鍵字參數)來彈性接收。

範例 4:統一計算任意個數的總和

def total(*numbers):
    """接受任意個數的數字,回傳它們的總和"""
    sum_ = 0
    for n in numbers:
        sum_ += n
    return sum_

print(total(1, 2, 3))          # 6
print(total(10, 20, 30, 40))   # 100

範例 5:把字典解包成關鍵字參數

def show_profile(name, age, **extra):
    """印出基本資料,並顯示額外資訊"""
    print(f"姓名: {name}, 年齡: {age}")
    for key, value in extra.items():
        print(f"{key}: {value}")

profile = {"city": "台北", "hobby": "登山"}
show_profile("小芳", 28, **profile)
# 輸出:
# 姓名: 小芳, 年齡: 28
# city: 台北
# hobby: 登山
  • *numbers 變成一個 tuple**extra 變成一個 dict
  • 在呼叫端使用 *** 可以解包序列或字典,讓參數傳遞更自然。

5. 匿名函式(lambda)

當需要簡短的單行函式時,lambda 表達式提供了快速的寫法,常見於 map()filter()sorted() 等高階函式中。

範例 6:使用 lambda 排序字串長度

words = ["Python", "資料科學", "AI", "機器學習"]
# 依字串長度由短到長排序
sorted_words = sorted(words, key=lambda w: len(w))
print(sorted_words)   # ['AI', 'Python', '資料科學', '機器學習']

注意lambda 只能寫單行表達式,若邏輯較複雜,仍建議使用 def 定義完整函式,以提升可讀性。


常見陷阱與最佳實踐

陷阱 可能的結果 建議的做法
可變物件作為預設參數 每次呼叫函式時,共用同一個物件,導致意外的狀態累積 使用 None 作為預設值,並在函式內部初始化:
def func(arg=None): if arg is None: arg = []
忘記 return 函式只執行副作用,呼叫端拿到 None,可能導致後續錯誤 確認函式的設計是否需要回傳值,若需要,務必寫 return;若不需要,使用明確的說明文件或 pass 表示空操作
參數順序混淆 位置參數與關鍵字參數混用時,可能拋出 TypeError 優先使用關鍵字參數,或在函式文件中明確列出參數順序與說明
過度使用 *args/**kwargs 失去函式的可讀性與自我說明性 僅在真的需要接受不確定數量參數時使用,平時盡量寫明確的參數列表
lambda 內部寫太多 讓程式碼變得難以閱讀與除錯 保持 lambda 簡潔,複雜邏輯拆成普通函式

最佳實踐

  1. 為每個函式寫 docstring:使用三引號撰寫說明,說明參數、回傳值與例外情況。
  2. 遵守 PEP 8 命名規範:函式名稱使用小寫加底線(snake_case)。
  3. 盡量讓函式保持單一職責(Single Responsibility Principle),每個函式只解決一件事。
  4. 使用型別註解(type hints)提升 IDE 輔助與程式可讀性:
    def add(a: int, b: int) -> int:
        return a + b
    
  5. 單元測試:為關鍵函式撰寫測試,確保未來改動不會破壞原有行為。

實際應用場景

  1. 資料前處理
    在資料科學專案中,常需要將 CSV 檔案的每一列資料轉換成特定格式。利用 def clean_row(row): 把清理邏輯封裝,配合 map(clean_row, rows) 可一次處理大量資料。

  2. API 請求封裝
    為了避免在程式的每個角落重複寫 requests.get(url, headers=...),可以寫一個 def fetch_json(endpoint, **params): 的通用函式,統一處理錯誤、重試機制與回傳格式。

  3. GUI 事件處理
    使用 tkinter 時,按鈕的 command 參數需要傳入一個可呼叫的函式。將每個按鈕的行為抽成獨立函式(或使用 lambda 包裝參數),可以讓 UI 程式碼保持乾淨。

  4. 自訂排序與過濾
    在電商平台的商品列表中,常需要依「價格」或「評分」排序。利用 sorted(products, key=lambda p: p['price']),或寫一個 def sort_by(key): return lambda item: item[key],讓排序邏輯可重用。

  5. 遞迴演算法
    計算階乘、斐波那契數列或樹狀結構遍歷,都離不開函式的遞迴呼叫。適當的基礎條件(base case)與參數傳遞,是避免無限遞迴的關鍵。


總結

函式是 Python 程式設計的基石,宣告 時使用 def呼叫 時只要提供相對應的參數,就能把複雜的工作分割成易於管理的小單元。掌握以下幾點,能讓你的程式更可讀、可維護、可擴充

  • 清晰的 函式名稱docstring,讓使用者一眼看懂功能。
  • 正確使用 位置參數、關鍵字參數、預設參數,提升呼叫的彈性。
  • 在需要時善用 *args、**kwargslambda,但別濫用以免失去可讀性。
  • 注意 可變預設值 的陷阱,並遵循 PEP 8 與型別註解的最佳實踐。

只要在寫程式的每一步都把 「把工作拆成小函式」 當成思考模式,你將會發現 Python 的開發效率與程式品質會有顯著的提升。祝你在 Python 的函式世界裡玩得開心、寫得順手!