本文 AI 產出,尚未審核

Python 函式(Functions)

參數類型:位置、具名、預設、*args、**kwargs


簡介

在 Python 中,函式是程式設計的基本建構塊。透過函式,我們可以把重複的程式碼抽象化、提升可讀性、降低維護成本。而 參數的傳遞方式 則是影響函式彈性與可用性的關鍵。

  • 位置參數(Positional arguments)讓呼叫者必須依序提供值,最直觀卻也最容易出錯。
  • 具名參數(Keyword arguments)則以 name=value 的形式指定,提升可讀性。
  • 預設參數(Default arguments)讓參數在呼叫時可以省略,為函式提供合理的預設行為。
  • 可變長度參數 *args**kwargs 則讓函式接受不確定數量的輸入,從簡單的工具函式到高度通用的 API,都離不開它們的身影。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,完整介紹這五種參數類型,幫助讀者在日常開發中寫出 更易讀、可維護且彈性十足 的 Python 函式。


核心概念

1. 位置參數(Positional Arguments)

位置參數是最基本的參數類型,呼叫函式時必須依照函式定義的順序提供對應的值。

def greet(name, age):
    """簡單的問候函式,使用位置參數"""
    print(f"Hello, {name}! 你今年 {age} 歲。")

# 呼叫方式
greet("阿明", 28)          # Hello, 阿明! 你今年 28 歲。
# 若順序錯誤,輸出會不符合預期
greet(28, "阿明")          # Hello, 28! 你今年 阿明 歲。

Tip:位置參數在參數數量不多且意義明確時最適合使用。


2. 具名參數(Keyword Arguments)

具名參數允許在呼叫時以 參數名=值 的方式指定,順序不再重要,程式碼可讀性大幅提升。

def greet(name, age):
    print(f"Hello, {name}! 你今年 {age} 歲。")

# 使用具名參數
greet(age=30, name="小華")   # Hello, 小華! 你今年 30 歲。
  • 優點
    • 呼叫者可自行決定參數順序。
    • 當函式參數較多時,能清楚表達每個值的意義。

3. 預設參數(Default Arguments)

在函式定義時給予參數預設值,呼叫者可以選擇性省略該參數。

def greet(name, age=18):
    """若未提供 age,預設為 18 歲"""
    print(f"Hello, {name}! 你今年 {age} 歲。")

greet("阿珍")                # Hello, 阿珍! 你今年 18 歲。
greet("阿珍", 22)            # Hello, 阿珍! 你今年 22 歲。

注意:預設值會在函式定義時被計算一次,若使用可變物件(如 list、dict)作為預設值,必須特別小心。


4. 可變長度位置參數:*args

*args 讓函式接受任意數量的位置參數,在函式內部會被收集為一個 tuple。

def total(*numbers):
    """計算任意個數字的總和"""
    result = sum(numbers)
    print(f"總和為 {result}")

total(1, 2, 3)               # 總和為 6
total(10, 20)                # 總和為 30
total()                      # 總和為 0
  • 應用
    • 建立彈性的統計函式、日誌收集器、指令列參數解析等。

5. 可變長度具名參數:**kwargs

**kwargs 讓函式接受任意數量的具名參數,在函式內部會被收集為一個 dict。

def profile(**info):
    """印出使用者的任意資訊"""
    for key, value in info.items():
        print(f"{key}: {value}")

profile(name="小美", age=25, city="台北")
# name: 小美
# age: 25
# city: 台北
  • 常見用途
    • 建立可擴充的設定函式、封裝第三方 API 的參數、動態生成 SQL 查詢等。

6. 同時使用 *args**kwargs

結合兩者可以同時接受不確定的位置具名參數。

def logger(level, *messages, **metadata):
    """簡易日誌函式,支援多條訊息與額外資訊"""
    print(f"[{level.upper()}] " + " | ".join(messages))
    if metadata:
        print("Metadata:", metadata)

logger("info", "系統啟動", "載入模組", user="admin", ip="192.168.1.10")
# [INFO] 系統啟動 | 載入模組
# Metadata: {'user': 'admin', 'ip': '192.168.1.10'}

常見陷阱與最佳實踐

陷阱 說明 解決方案
預設值使用可變物件 def f(a=[]): 會導致多次呼叫共享同一個 list。 使用 None 作為預設值,然後在函式內部初始化:
def f(a=None): a = [] if a is None else a
***args、kwargs 的位置 *args 必須放在所有位置參數之後,**kwargs 必須放在最後。 正確順序:def func(pos1, pos2, *args, kw1=None, **kwargs):
混用位置與具名參數時的衝突 同一個參數同時以位置與具名方式傳入會拋出 TypeError 確認呼叫時不要重複指定同一參數。
過度使用 kwargs 失去型別檢查 讓函式變得過於寬鬆,難以追蹤錯誤。 若參數明確,盡量使用具名參數或 TypedDictdataclass 來描述結構。
*args 變成空 tuple 呼叫時忘記傳入參數,導致 *args 為空,需自行處理。 在函式內部使用 if not args: 進行預設行為或拋出自訂例外。

最佳實踐

  1. 先寫位置參數,再加入具名與預設值:保持 API 的簡潔與可讀。
  2. 預設值盡量使用不可變類型(如 intstrtuple),避免共享狀態。
  3. 文件化參數:使用 docstring 說明每個參數的型別與用途,尤其是 *args**kwargs
  4. 適度限制 **kwargs:若只接受特定鍵,使用 **kwargs 再配合驗證(如 if not set(kwargs) <= allowed_keys:)。
  5. 利用型別提示(type hints)def func(*args: int, **kwargs: str) -> None: 能在 IDE 中即時捕捉錯誤。

實際應用場景

1. 建立通用的 API 請求函式

import requests

def api_get(url, *params, timeout=5, **headers):
    """簡化 GET 請求,支援可變的查詢參數與自訂標頭"""
    # 組合查詢字串
    query = "&".join(f"{k}={v}" for k, v in enumerate(params, start=1))
    full_url = f"{url}?{query}" if query else url
    response = requests.get(full_url, timeout=timeout, headers=headers)
    return response.json()

# 呼叫範例
data = api_get(
    "https://api.example.com/users",
    "active", "admin",          # *params 變成 ("active", "admin")
    timeout=10,
    Authorization="Bearer abc123",
    Accept="application/json"
)
  • 為什麼使用 *params**headers
    • *params 允許任意數量的查詢條件,未來若需求改變只要多加參數即可。
    • **headers 讓呼叫者自行決定要送哪些 HTTP 標頭,保持函式的通用性。

2. 動態產生 SQL 查詢

def select(table, **conditions):
    """根據條件動態產生 SELECT 語句,避免硬編碼"""
    if not conditions:
        where_clause = ""
    else:
        placeholders = " AND ".join(f"{col}= %s" for col in conditions)
        where_clause = f"WHERE {placeholders}"
    sql = f"SELECT * FROM {table} {where_clause};"
    values = tuple(conditions.values())
    return sql, values

# 使用範例
query, params = select("employees", department="IT", status="active")
print(query)   # SELECT * FROM employees WHERE department= %s AND status= %s;
print(params)  # ('IT', 'active')
  • 好處:利用 **conditions 自動映射欄位與值,減少手寫錯誤與 SQL Injection 風險(配合參數化查詢)。

3. 通用的資料驗證函式

def validate(**fields):
    """檢查必填欄位是否為空,回傳錯誤訊息列表"""
    errors = []
    for name, value in fields.items():
        if value in (None, "", [], {}):
            errors.append(f"欄位「{name}」不可為空")
    return errors

# 呼叫
errs = validate(username="alice", password="", email=None)
# ['欄位「password」不可為空', '欄位「email」不可為空']
  • 應用:在表單或 API 請求的前置驗證階段,只要把收到的字典展開傳入即可:validate(**request.json)

總結

  • 位置參數是最直接的傳值方式,適合參數數量固定且意義明確的情境。
  • 具名參數提升呼叫的可讀性,讓程式碼更易於維護。
  • 預設參數提供合理的缺省行為,但要避免使用可變物件作為預設值。
  • *args 讓函式接受不定長度的位置參數,常用於統計、日誌、指令列工具等。
  • **kwargs 則收集不定長度的具名參數,適合建構彈性的 API、設定或動態查詢。
  • 同時使用 *args**kwargs 能讓函式兼容各種呼叫方式,但也要小心 過度寬鬆 帶來的型別安全與維護問題。

掌握這五種參數類型的特性與最佳實踐,能讓你在開發 Python 程式時 寫出更清晰、彈性更高且易於擴充 的函式。未來無論是寫小型腳本、建立大型服務,或是設計通用套件,這些概念都是不可或缺的基礎。祝你在 Python 的旅程中玩得開心、寫得更好!