Skip to content

Python 容器:List & Tuple

在 Python 編程中,你經常需要處理一組相關的數據。與其為每個數據創建一個獨立的變數,不如使用「容器資料型態」(Collections)來將它們組織在一起。容器就像一個盒子,可以讓你儲存和管理多個項目。

Python 內置了四種主要的容器資料型態,每種都有其獨特的特性和用途:

  1. List:有序、可變的集合。
  2. Tuple:有序、但不可變的集合。
  3. Dict (Dictionary):無序(在 Python 3.7+ 版本後為插入順序)的鍵值對(key-value pair)集合。
  4. Set:無序、且不含重複元素的集合。

本文集中討論兩種容器類型:ListTuple。由於它們在結構上非常相似,將它們放在一起學習可以幫助你更清楚地理解它們的核心差異,特別是「可變性」(mutability)的概念。

🐍 深入了解 List

List(列表)是 Python 中最靈活的容器之一。當你需要一個可以隨時新增、刪除或修改內容的數據集合時,List 就是你的首選。

一個 List 具有以下特點:

  • 有序(Ordered):項目在 List 中的位置是固定的,除非你手動改變它。my_list[0] 永遠是第一個項目。
  • 可變(Mutable):創建 List 後,可隨意增加、刪除或修改其中的項目。
  • 可包含任何類型的 Object:你可以在同一個 List 中放入整數(int)、浮點數(float)、字串(string)、None,甚至是另一個 ListTuple,形成嵌套結構(nested structure)。

創建一個 List

創建 List 非常簡單,只需用方括號 [] 將項目括起來,並用逗號 , 分隔即可。

python
# 一個空的 List
empty_list = [] # 或 empty_list = list()
print(empty_list)

# 包含不同資料類型的 List
mixed_list = [1, "Hello", 3.14, True, None]
print(mixed_list)

# 巢狀 List
nested_list = [1, 2, ["a", "b", "c"]]
print(nested_list)

# 也可以使用 list() 構造函數
from_tuple = list((1, 2, 3)) # 從一個 tuple 創建 list
print(from_tuple)

存取 List 中的項目

你可以透過索引(index)來存取 List 中的特定項目。Python 的索引是從 0 開始的。

python
fruits = ["apple", "banana", "cherry", "orange"]

# 存取第一個項目
print(fruits[0])  # Output: apple

# 存取最後一個項目 (使用負數索引)
print(fruits[-1]) # Output: orange

# 切片 (Slicing): 獲取一個子列表
# 獲取索引 1 到 3 (不包含 3) 的項目
print(fruits[1:3]) # Output: ['banana', 'cherry']

修改、新增與刪除項目

這是 List 最強大的功能——可變性。

python
numbers = [1, 2, 3, 4, 5]

# 修改項目:將索引為 2 的項目從 3 改為 30
numbers[2] = 30
print(f"updated: {numbers}") # updated: [1, 2, 30, 4, 5]

# 新增項目到末尾 (append)
numbers.append(6)
print(f"append 6: {numbers}") # append 6: [1, 2, 30, 4, 5, 6]

# 在指定位置插入項目 (insert)
# 在索引 1 的位置插入 99
numbers.insert(1, 99)
print(f"insert 99: {numbers}") # insert 99: [1, 99, 2, 30, 4, 5, 6]

# 刪除項目 (remove, pop, del)
# remove: 刪除第一個符合的數值
numbers.remove(30) 
print(f"remove 30: {numbers}") # remove 30: [1, 99, 2, 4, 5, 6]

# pop: 刪除並返回指定索引的項目 (預設是最後一個)
popped_item = numbers.pop()
print(f"list: {numbers}, popped: {popped_item}") # list: [1, 99, 2, 4, 5], popped: 6

# del: 刪除指定索引的項目
del numbers[0]
print(f"del numbers[0]: {numbers}") # del numbers[0]: [99, 2, 4, 5]

檢查項目是否存在

使用 in 關鍵字來快速檢查一個項目是否存在於 List 中。

python
fruits = ["apple", "banana", "cherry"]

if "banana" in fruits:
    print("Yes, 'banana' is in the fruits list.")

if "mango" not in fruits:
    print("No, 'mango' is not in the fruits list.")

遍歷 List (Looping)

使用 for 迴圈是處理 List 中每一個項目的標準做法。

python
# 方法一:直接遍歷項目
for fruit in fruits:
    print(fruit)

# 方法二:使用 enumerate 同時獲取索引和項目
for index, fruit in enumerate(fruits):
    print(f"Index {index}: {fruit}")

enumerate 在你需要知道項目位置時特別有用。

列表推導式 List Comprehension

List Comprehension 提供了一種更簡潔、更具 Python 風格的方式來創建 List。它將 for 迴圈和創建新項目的代碼合併成一行。

假設你想創建一個包含 0 到 9 平方的 List

python
# 傳統方法
squares = []
for i in range(10):
    squares.append(i * i)
print(squares)

# List Comprehension 方法
squares_comp = [i * i for i in range(10)]
print(squares_comp)

兩種方法結果完全相同,但 List Comprehension 明顯更簡潔易讀。

List 作為函數參數

一個非常重要的概念:當把 List 傳遞給一個函數時,傳遞的是對該 List 的引用(reference)。如果在函數內部修改了 List,原來的 List 也會被改變,即使函數沒有 return 任何東西。

python
def add_item_to_list(a_list):
    print(f"函數內部 (修改前): {a_list}")
    a_list.append("New Item")
    print(f"函數內部 (修改後): {a_list}")

my_data = [1, 2, 3]
print(f"函數外部 (調用前): {my_data}")

add_item_to_list(my_data)

print(f"函數外部 (調用後): {my_data}") # 注意!my_data 已經被改變了

輸出結果:

函數外部 (調用前): [1, 2, 3]
函數內部 (修改前): [1, 2, 3]
函數內部 (修改後): [1, 2, 3, 'New Item']
函數外部 (調用後): [1, 2, 3, 'New Item']

這個特性很強大,但也可能導致意想不到的副作用。如果你不希望原始 List 被修改,可以傳遞它的一個副本,例如 add_item_to_list(my_data.copy())

🔗 不可變的夥伴 Tuple

Tuple(元組)可以被看作是 List 的「唯讀」(read-only)版本。它的結構和 List 非常相似,都是有序的序列,但核心區別在於:Tuple 是不可變的(Immutable)

一旦你創建了一個 Tuple,就無法再修改它的內容——不能新增、刪除或更改任何項目。

python
my_tuple = (1, 2, 3)

# 嘗試修改 Tuple 中的項目
try:
    my_tuple[0] = 100
except TypeError as e:
    print(e) # Output: 'tuple' object does not support item assignment

為何及何時使用 Tuple?

既然 List 這麼靈活,為什麼還需要 Tuple

  1. 數據完整性:當你有一組不想被改變的數據時,使用 Tuple 可以防止意外修改。
  2. 性能:由於 Tuple 是不可變的,Python 在內部可以進行一些優化。通常情況下,Tuple 佔用的記憶體比 List 少,處理速度也稍快。

創建一個 Tuple

創建 Tuple 使用圓括號 ()

python
# 創建一個 Tuple
person = ("Alice", 30, "Engineer")
print(person)

# 創建 Tuple 時,括號是可選的
person_no_paren = "Bob", 25, "Designer"
print(type(person_no_paren)) # Output: <class 'tuple'>

# 特殊情況:創建只有一個項目的 Tuple
# 錯誤示範,這不是一個 Tuple,而是一個整數
not_a_tuple = (1)
print(type(not_a_tuple)) # Output: <class 'int'>

# 正確示範,必須加上一個逗號
is_a_tuple = (1,)
print(type(is_a_tuple)) # Output: <class 'tuple'>

記住: 創建單項目 Tuple 的關鍵是逗號 ,,而不是括號 ()

Tuple Unpacking

Tuple 一個非常方便的特性是「解包」(Unpacking)。你可以將 Tuple 中的項目一次性賦值給多個變數。

python
# 基本的 Unpacking
lon, lat = (120.9, 23.5)

print(f"Longitude: {lon}") # Output: Longitude: 120.9
print(f"Latitude: {lat}")  # Output: Latitude: 23.5

這個特性在函數返回多個值時特別有用。Python 函數返回多個值時,實際上是將它們打包成一個 Tuple

python
def get_user_stats(user_id):
    # 假設從數據庫獲取數據
    name = "Charles"
    posts = 152
    followers = 890
    return name, posts, followers # 打包成一個 Tuple

# Unpacking a function's return value
user_name, num_posts, num_followers = get_user_stats(1)

print(f"User: {user_name}, Posts: {num_posts}, Followers: {num_followers}")

交換變數的技巧

利用 Tuple 的打包和解包特性,你可以用一行代碼優雅地交換兩個變數的值。

python
a = 10
b = 20

# 傳統方法需要一個臨時變數
# temp = a
# a = b
# b = temp

# Pythonic way using tuple packing/unpacking
a, b = b, a

print(f"a = {a}, b = {b}") # Output: a = 20, b = 10

Tuple 的操作

Tuple 支援所有不涉及修改的操作,這些操作和 List 完全一樣:

  • 索引: my_tuple[0]
  • 切片: my_tuple[1:3]
  • 檢查存在: if item in my_tuple:
  • 遍歷: for item in my_tuple:
  • 獲取長度: len(my_tuple)

任何試圖修改 Tuple 的操作(如 append, insert, removemy_tuple[i] = value)都會導致錯誤。

🤔 List vs. Tuple:如何選擇?

現在你已經了解了 ListTuple,這裡有一個簡單的總結來幫助你做出選擇:

特性List (列表)Tuple (元組)
可變性Mutable (可變)Immutable (不可變)
語法[1, 2, 3](1, 2, 3)
用途適合儲存需要動態修改的數據集合適合儲存不應被改變的數據,如常量、函數返回值、字典鍵
性能彈性較大,但記憶體和性能開銷稍高輕量、高效,數據安全

經驗法則:

  • 如果你的數據集合在程式運行期間需要改變大小或內容,請使用 List
  • 如果你的數據代表一組固定的、相關聯的項目,使用 Tuple 會更安全、更高效。
  • 預設情況下,可以先考慮使用 Tuple。只有當你確定需要修改數據時,再選擇 List

掌握 ListTuple 是學習 Python 的關鍵一步。它們是構建更複雜數據結構和算法的基礎。在下一篇文章中,將會繼續探討另外兩個強大的容器:DictSet

📚 參考資料

KF Software House