本文 AI 產出,尚未審核

Python 資料結構 – 元組(tuple)

主題:解構賦值(Tuple Unpacking)


簡介

在 Python 中,元組(tuple) 是一種不可變(immutable)的序列類型,常被用來一次傳遞多個值。除了直接存取索引外,最常見且最實用的操作之一,就是解構賦值(亦稱「序列解包」)。透過解構賦值,我們可以把元組裡的每個元素直接展開成獨立的變數,讓程式碼更簡潔、可讀性更高。

解構賦值不僅限於元組,同樣適用於 list、set 甚至是自訂的可迭代物件。掌握這項技巧,能讓你在處理函式回傳多值、資料分割、迭代等情境時,寫出更「Pythonic」的程式碼。本文將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你深入了解解構賦值的威力。


核心概念

1. 基本語法

解構賦值的語法非常直觀,只要左側寫上變數名稱,右側提供一個可迭代物件(如 tuple),Python 會自動把元素一一對應:

# 基本的 tuple 解構賦值
point = (3, 5)
x, y = point          # x 取得 3, y 取得 5
print(x, y)          # 輸出: 3 5

重點:左側變數的個數必須與右側可迭代物件的元素數量相同,否則會拋出 ValueError


2. 使用星號(*)收集剩餘元素

有時我們只關心前幾個元素,或想把「剩下的」全部收集到一個 list 中,這時就可以使用 星號運算子 *

# 把多餘的元素收集到 one_list 中
numbers = (1, 2, 3, 4, 5)
first, second, *rest = numbers
print(first)   # 1
print(second)  # 2
print(rest)    # [3, 4, 5]
  • *rest 必須放在左側變數的 最後,或在 中間(但只能有一個星號變數)。
  • 收集的結果永遠是 list,即使原始資料是 tuple。

3. 解構嵌套結構

當元組內部還包含其他序列時,我們可以一次解構多層結構:

# 嵌套解構
person = ("Alice", (25, "女"))
name, (age, gender) = person
print(name)   # Alice
print(age)    # 25
print(gender) # 女

這種寫法在處理 JSONCSV資料庫查詢結果 時特別有用,能直接把欄位對應到變數。


4. 與函式回傳值結合

函式可以同時回傳多個值(實際上是回傳一個 tuple),配合解構賦值即可直接取得每個結果:

def min_max(nums):
    """回傳列表中的最小值與最大值"""
    return min(nums), max(nums)

values = [4, 7, 2, 9, 5]
minimum, maximum = min_max(values)
print(f"最小值: {minimum}, 最大值: {maximum}")
# 輸出: 最小值: 2, 最大值: 9

5. 交換變數的技巧(無需暫存變數)

在其他語言中,交換兩個變數通常需要額外的暫存變數;在 Python,利用 tuple 解構賦值即可一行搞定:

a = 10
b = 20
a, b = b, a   # 直接交換
print(a, b)   # 20 10

此技巧在演算法(如排序)或 迭代器 中頻繁出現,寫法簡潔且不會產生副作用。


程式碼範例(實用示例)

以下提供 5 個常見且實務導向的範例,說明解構賦值在不同情境下的應用。

範例 1:同時讀取字典的鍵值對

# 把 dict 的 items 直接解構成 key, value
scores = {"Tom": 85, "Jane": 92, "Mike": 78}
for name, grade in scores.items():
    print(f"{name} 的成績是 {grade}")
# 輸出:
# Tom 的成績是 85
# Jane 的成績是 92
# Mike 的成績是 78

註解dict.items() 會回傳 (key, value) 形式的 tuple,直接解構讓迴圈更直觀。


範例 2:一次取得檔案路徑的目錄與檔名

import os

path = "/home/user/data/report.csv"
directory, filename = os.path.split(path)
print(f"目錄: {directory}")
print(f"檔名: {filename}")
# 輸出:
# 目錄: /home/user/data
# 檔名: report.csv

使用 os.path.split 回傳的 tuple,配合解構賦值即可一次得到兩個資訊。


範例 3:從 API 回傳的 JSON 中抽取特定欄位

import json

response = '{"id": 123, "name": "Widget", "price": 19.99, "stock": 57}'
data = json.loads(response)

# 只取出 id, name, price
product_id, product_name, product_price = data["id"], data["name"], data["price"]
print(product_id, product_name, product_price)
# 輸出: 123 Widget 19.99

若資料結構較複雜,可先建立 tuple 再一次解構,保持程式碼的可讀性


範例 4:使用 * 收集不定長度的參數

def stats(*numbers):
    """回傳最小值、最大值與其餘所有數字的平均值"""
    if not numbers:
        return None
    minimum, maximum, *rest = numbers
    avg_rest = sum(rest) / len(rest) if rest else 0
    return minimum, maximum, avg_rest

print(stats(3, 7, 2, 9, 5))
# 輸出: (3, 7, 5.333333333333333)

*rest 讓函式能接受任意長度的參數,同時仍能直接取出前兩個值。


範例 5:在排序演算法中同時交換元素

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                # 直接交換,不需要暫存變數
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

print(bubble_sort([5, 2, 9, 1, 5, 6]))
# 輸出: [1, 2, 5, 5, 6, 9]

此寫法在 排序排列等演算法中非常常見,既簡潔又避免了多餘的暫存。


常見陷阱與最佳實踐

陷阱 說明 解決方式
元素數量不匹配 左側變數少於或多於右側元素時會拋 ValueError 使用 * 收集多餘元素或確認資料長度。
星號變數位置錯誤 *var 只能出現在左側一次,且不能同時放在最左或最右兩端(除非只有一個)。 確認星號變數唯一且位置正確。
可迭代物件不是 tuple/list 某些自訂物件未實作 __iter__,會導致解構失敗。 確保物件實作 __iter__ 或先轉成 list/tuple
意外改變可變物件 解構賦值本身不會改變原始資料,但若使用 *rest 收集到 list,後續修改會影響該 list。 如需保持不變,使用 tuple(rest) 轉為不可變。
過度解構降低可讀性 一行解構太多層次會讓程式碼難以閱讀。 盡量保持解構層級在 2 層以內,必要時分步解構。

最佳實踐

  1. 明確命名:變數名稱要能說明其意圖,例如 x, y = point 而非 a, b
  2. 使用 * 收集:當不確定資料長度時,利用 * 防止 ValueError
  3. 保持簡潔:一次解構不超過 3~4 個變數,過長的解構應拆成多行。
  4. 結合函式回傳:設計函式時,直接回傳 tuple,讓呼叫端可以使用解構賦值取得多個結果。
  5. 避免對 *rest 直接修改:若需要保留原始資料,先做淺拷貝或轉為 tuple。

實際應用場景

  1. 資料前處理
    在機器學習或資料分析前,常需要把 CSV 檔的每一列拆成 id, features, label,解構賦值讓程式碼一目了然。

  2. 網路通信
    解析 HTTP 回傳的 status_code, headers, body,直接解構成三個變數,方便後續判斷與處理。

  3. 多值回傳的 API
    設計自訂函式或類別方法時,回傳 (result, error_msg),呼叫端可直接 result, err = func(),不必額外檢查索引。

  4. 演算法實作
    如快速排序、堆疊/佇列操作,常用解構來同時取得元素與剩餘結構,提升效能與可讀性。

  5. 日誌與除錯
    timestamp, level, message 解構後直接寫入日誌檔,省去索引的繁瑣。


總結

解構賦值是 Python 中一項 簡潔且功能強大 的語法糖,讓我們能夠:

  • 快速展開 tuple、list 或其他可迭代物件。
  • 靈活收集 多餘元素 (*rest)。
  • 直接交換 變數而不需要暫存。
  • 提升程式碼可讀性,特別在函式回傳多值、資料前處理與演算法實作時。

掌握這項技巧後,你將在日常開發中減少大量的索引與暫存程式碼,寫出更 Pythonic、更易維護的程式。記得遵守最佳實踐,避免常見陷阱,讓解構賦值成為你手中最得心應手的工具吧!