Python 容器:List & Tuple
在 Python 編程中,你經常需要處理一組相關的數據。與其為每個數據創建一個獨立的變數,不如使用「容器資料型態」(Collections)來將它們組織在一起。容器就像一個盒子,可以讓你儲存和管理多個項目。
Python 內置了四種主要的容器資料型態,每種都有其獨特的特性和用途:
- List:有序、可變的集合。
- Tuple:有序、但不可變的集合。
- Dict (Dictionary):無序(在 Python 3.7+ 版本後為插入順序)的鍵值對(key-value pair)集合。
- Set:無序、且不含重複元素的集合。
本文集中討論兩種容器類型:List
和 Tuple
。由於它們在結構上非常相似,將它們放在一起學習可以幫助你更清楚地理解它們的核心差異,特別是「可變性」(mutability)的概念。
🐍 深入了解 List
List
(列表)是 Python 中最靈活的容器之一。當你需要一個可以隨時新增、刪除或修改內容的數據集合時,List
就是你的首選。
一個 List
具有以下特點:
- 有序(Ordered):項目在
List
中的位置是固定的,除非你手動改變它。my_list[0]
永遠是第一個項目。 - 可變(Mutable):創建
List
後,可隨意增加、刪除或修改其中的項目。 - 可包含任何類型的 Object:你可以在同一個
List
中放入整數(int)、浮點數(float)、字串(string)、None
,甚至是另一個List
或Tuple
,形成嵌套結構(nested structure)。
創建一個 List
創建 List
非常簡單,只需用方括號 []
將項目括起來,並用逗號 ,
分隔即可。
# 一個空的 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 開始的。
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
最強大的功能——可變性。
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
中。
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
中每一個項目的標準做法。
# 方法一:直接遍歷項目
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
。
# 傳統方法
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
任何東西。
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
,就無法再修改它的內容——不能新增、刪除或更改任何項目。
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
?
- 數據完整性:當你有一組不想被改變的數據時,使用
Tuple
可以防止意外修改。 - 性能:由於
Tuple
是不可變的,Python 在內部可以進行一些優化。通常情況下,Tuple
佔用的記憶體比List
少,處理速度也稍快。
創建一個 Tuple
創建 Tuple
使用圓括號 ()
。
# 創建一個 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
中的項目一次性賦值給多個變數。
# 基本的 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
。
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
的打包和解包特性,你可以用一行代碼優雅地交換兩個變數的值。
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
, remove
或 my_tuple[i] = value
)都會導致錯誤。
🤔 List vs. Tuple:如何選擇?
現在你已經了解了 List
和 Tuple
,這裡有一個簡單的總結來幫助你做出選擇:
特性 | List (列表) | Tuple (元組) |
---|---|---|
可變性 | Mutable (可變) | Immutable (不可變) |
語法 | [1, 2, 3] | (1, 2, 3) |
用途 | 適合儲存需要動態修改的數據集合 | 適合儲存不應被改變的數據,如常量、函數返回值、字典鍵 |
性能 | 彈性較大,但記憶體和性能開銷稍高 | 輕量、高效,數據安全 |
經驗法則:
- 如果你的數據集合在程式運行期間需要改變大小或內容,請使用
List
。 - 如果你的數據代表一組固定的、相關聯的項目,使用
Tuple
會更安全、更高效。 - 預設情況下,可以先考慮使用
Tuple
。只有當你確定需要修改數據時,再選擇List
。
掌握 List
和 Tuple
是學習 Python 的關鍵一步。它們是構建更複雜數據結構和算法的基礎。在下一篇文章中,將會繼續探討另外兩個強大的容器:Dict
和 Set
。