Python 元组

元组(tuple)是列表的"亲兄弟"——几乎一模一样,只有一个关键区别:元组不可变(创建后不能增删改)。这听起来像是个限制,实际上却非常有用。这一章我们看为什么 Python 要有这个"残缺版列表"。

1. 什么是元组?

元组是有序、不可变的序列,用圆括号 () 创建。它和列表一样支持索引、切片、遍历、解包,但不能修改元素。

# 元组:有序、不可变的序列,用圆括号 () 创建
point = (3, 4)
colors = ("red", "green", "blue")
empty = ()
single = (42,)          # ⚠ 单元素元组必须加逗号!

# 不加括号也能创建(叫"元组打包")
t = 1, 2, 3
print(t)                # (1, 2, 3)
print(type(t))          # <class 'tuple'>

# 用 tuple() 转换
print(tuple([1, 2, 3]))     # (1, 2, 3)   列表转元组
print(tuple("hello"))       # ('h', 'e', 'l', 'l', 'o')
print(tuple(range(5)))      # (0, 1, 2, 3, 4)

# 单元素元组的陷阱!
x = (5)         # 这不是元组,就是整数 5
y = (5,)        # 这才是单元素元组
print(type(x))  # <class 'int'>
print(type(y))  # <class 'tuple'>

单元素元组的陷阱:(5) 只是数字 5(小括号被当成数学分组),(5,) 才是元组。新手常踩坑,务必记住加逗号。

2. 元组的不可变性

t = (1, 2, 3)

# 1. 索引和切片,和列表完全一样
print(t[0])         # 1
print(t[-1])        # 3
print(t[1:])        # (2, 3)
print(len(t))       # 3

# 2. 不可变!不能改、不能加、不能删
# t[0] = 99         # ❌ TypeError: 'tuple' object does not support item assignment
# t.append(4)       # ❌ AttributeError: tuple 没有 append
# del t[0]          # ❌ TypeError

# 3. 但可以整个重新赋值(创建新元组)
t = (4, 5, 6)       # t 指向新元组,旧的 (1,2,3) 被回收
print(t)            # (4, 5, 6)

# 4. 元组里的"可变元素"仍然可以改
nested = (1, [2, 3], 4)
nested[1].append(99)         # 改的是列表,不是元组本身
print(nested)                # (1, [2, 3, 99], 4)

# 5. 元组的运算:+ 拼接,* 重复,和列表一样
print((1, 2) + (3, 4))       # (1, 2, 3, 4)
print((1, 2) * 3)            # (1, 2, 1, 2, 1, 2)

"不可变"的含义:

3. 打包与解包

这是元组最常用的特性。把多个值放进元组叫"打包",反过来叫"解包"。

# 元组解包(tuple unpacking),Python 强大的特性之一

# 1. 基本解包
point = (3, 4, 5)
x, y, z = point
print(x, y, z)          # 3 4 5

# 2. 值交换(经典 Python 写法)
a, b = 1, 2
a, b = b, a
print(a, b)             # 2 1

# 3. 用 * 收集多余元素
first, *rest = (1, 2, 3, 4, 5)
print(first)            # 1
print(rest)             # [2, 3, 4, 5]   注意是列表

*init, last = (1, 2, 3, 4, 5)
print(init)             # [1, 2, 3, 4]
print(last)             # 5

first, *middle, last = (1, 2, 3, 4, 5)
print(middle)           # [2, 3, 4]

# 4. 忽略某些值(用 _ 约定)
name, _, age = ("小明", "男", 20)    # 不要性别
print(name, age)        # 小明 20

# 5. 遍历键值对时解包(超常用)
user = {"name": "小明", "age": 20}
for key, value in user.items():
    print(f"{key}: {value}")

解包是 Python 的优雅利器:

4. 函数返回多个值(经典用法)

很多语言函数只能返回一个值,要返回多个得封装成对象或结构体。Python 借助元组,优雅地返回多个值。

# 元组最常见用途:函数返回多个值
def min_max(numbers):
    """同时返回最小值和最大值"""
    return min(numbers), max(numbers)   # 返回元组(省略了括号)

result = min_max([3, 1, 4, 1, 5, 9])
print(result)           # (1, 9)
print(type(result))     # <class 'tuple'>

# 直接解包接收(更优雅)
low, high = min_max([3, 1, 4, 1, 5, 9])
print(f"最小:{low},最大:{high}")
# 输出: 最小:1,最大:9

# 另一个例子:返回状态和数据
def get_user(user_id):
    if user_id == 1:
        return True, {"name": "小明"}    # 成功
    return False, None                    # 失败

ok, data = get_user(1)
if ok:
    print(f"找到用户:{data['name']}")
# 输出: 找到用户:小明

这种 return True, datareturn ok, value 的写法在 Python 标准库里随处可见,是 Pythonic 风格的重要部分。

5. 命名元组 namedtuple

普通元组用下标访问(p[0]),可读性差。命名元组给字段起名字,既能像对象一样 p.x 访问,又保持元组的轻量和不可变。

# 命名元组 namedtuple:给元组字段起名字,更易读
from collections import namedtuple

# 创建一个"点"类型
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

# 既能用名字访问,也能用索引访问
print(p.x, p.y)         # 3 4     <- 用名字(推荐,可读)
print(p[0], p[1])       # 3 4     <- 用索引

# 解包也支持
x, y = p
print(x, y)             # 3 4

# 实用例子:表示一条记录
Student = namedtuple("Student", ["name", "age", "score"])
students = [
    Student("小明", 20, 85),
    Student("小红", 19, 92),
    Student("小刚", 21, 78),
]

for s in students:
    print(f"{s.name}, {s.age} 岁,分数 {s.score}")

命名元组是简单数据类的绝佳选择——比普通类写法少,比字典更结构化,而且不可变(更安全)。处理 CSV、数据库记录时尤其好用。

6. 元组 vs 列表:什么时候用哪个?

既然列表能干所有事,为什么还要元组?有几个理由:

经验法则:数据可能变,用列表;数据不会变,用元组

7. 元组作为字典 key

# 元组能做字典的 key,列表不行
locations = {
    (31, 121): "上海",
    (39, 116): "北京",
    (23, 113): "广州",
}

print(locations[(31, 121)])   # 上海

# 列表不能做 key(因为可变、不可哈希)
# d = {[1, 2]: "x"}   # ❌ TypeError: unhashable type: 'list'

小结

元组是"不可变列表"。重点掌握:创建(注意单元素要加逗号)、不可变(但元素可变则内部可改)、解包(Python 优雅的核心,值交换、多返回值)、命名元组(轻量数据类)、何时用元组(不可变、可哈希、省内存、语义清晰)。下一篇看字典——Python 里处理键值数据的利器。

← 上一篇 Python 列表

下一篇 Python 字典

✈️💬