元组(tuple)的基本用法

元组不可修改

逗号 , 才是创建元组的关键,括号是可选的(除了空元组)。以下也是元组

列表采用中括号[]

1
2
3
4
5
6
7
# 元组是不可变的
DIRS = (0, 1), (1, 0), (0, -1), (-1, 0)
# DIRS[0] = (1, 0) # ❌ 错误!元组不可修改

# 如果是列表就可以修改
DIRS_LIST = [[0, 1], [1, 0], [0, -1], [-1, 0]]
DIRS_LIST[0] = [1, 0] # ✅ 可以修改

Python 元组完全入门指南

创建元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1. 用圆括号创建(最常用)
t1 = (1, 2, 3, 4, 5)
print(t1) # (1, 2, 3, 4, 5)

# 2. 不用括号,直接用逗号分隔
t2 = 1, 2, 3, 4, 5
print(t2) # (1, 2, 3, 4, 5)

# 3. 单元素元组(必须加逗号!)
t3 = (1,) # 正确,这是一个元组
print(t3) # (1,)
print(type(t3)) # <class 'tuple'>

not_tuple = (1) # 错误!这是整数1,不是元组
print(type(not_tuple)) # <class 'int'>

# 4. 空元组
t4 = ()
print(t4) # ()

# 5. 用 tuple() 函数创建
t5 = tuple([1, 2, 3]) # 列表转元组
print(t5) # (1, 2, 3)

t6 = tuple("hello") # 字符串转元组
print(t6) # ('h', 'e', 'l', 'l', 'o')

二、元组的基本操作

1. 访问元素(和列表一样)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
t = (10, 20, 30, 40, 50)

# 通过索引访问
print(t[0]) # 10(第一个元素)
print(t[2]) # 30(第三个元素)
print(t[-1]) # 50(最后一个元素)
print(t[-2]) # 40(倒数第二个)

# 切片操作
print(t[1:4]) # (20, 30, 40)
print(t[:3]) # (10, 20, 30)
print(t[2:]) # (30, 40, 50)
print(t[-3:]) # (30, 40, 50)
print(t[::2]) # (10, 30, 50) 步长为2

2. 元组是不可变的

1
2
3
4
5
6
7
8
9
10
11
t = (1, 2, 3)

# ❌ 这些操作会报错
# t[0] = 100 # TypeError: 'tuple' object does not support item assignment
# t.append(4) # AttributeError: 'tuple' object has no attribute 'append'
# t.remove(2) # AttributeError: 'tuple' object has no attribute 'remove'
# del t[0] # TypeError: 'tuple' object doesn't support item deletion

# ✅ 可以整体重新赋值
t = (4, 5, 6) # 这是创建新元组,不是修改原元组
print(t) # (4, 5, 6)

3. 元组的操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 拼接 +
t1 = (1, 2, 3)
t2 = (4, 5, 6)
t3 = t1 + t2
print(t3) # (1, 2, 3, 4, 5, 6)

# 重复 *
t4 = (1, 2) * 3
print(t4) # (1, 2, 1, 2, 1, 2)

# 成员判断 in / not in
print(2 in (1, 2, 3)) # True
print(5 not in (1, 2, 3)) # True

# 比较运算(逐个元素比较)
print((1, 2, 3) == (1, 2, 3)) # True
print((1, 2, 3) < (1, 3, 2)) # True(2 < 3)
print((1, 2) < (1, 2, 3)) # True(短的小于长的)

三、元组常用方法

元组只有两个方法,因为不可变所以方法很少:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
t = (1, 2, 3, 2, 4, 2, 5)

# 1. count() - 统计元素出现次数
print(t.count(2)) # 3(数字2出现了3次)
print(t.count(1)) # 1
print(t.count(9)) # 0

# 2. index() - 查找元素索引
print(t.index(2)) # 1(第一个2的位置)
print(t.index(2, 2)) # 3(从索引2开始找第一个2)

# 其他通用函数
print(len(t)) # 7 长度
print(max(t)) # 5 最大值
print(min(t)) # 1 最小值
print(sum(t)) # 18 总和

四、元组的拆包(解构赋值)

这是元组最强大的特性之一!

1. 基本拆包

1
2
3
4
5
6
7
8
9
10
# 把元组的值赋给多个变量
point = (3, 5)
x, y = point # 等价于 x = 3, y = 5
print(f"x={x}, y={y}") # x=3, y=5

# 交换变量(经典用法)
a, b = 10, 20
print(f"a={a}, b={b}") # a=10, b=20
a, b = b, a # 一行交换
print(f"a={a}, b={b}") # a=20, b=10

2. 用 _ 忽略不需要的值

1
2
3
4
5
6
7
8
9
10
person = ("Alice", 25, "工程师", "北京")
name, age, _, city = person # 忽略第三个字段
print(f"{name}, {age}岁, 住在{city}") # Alice, 25岁, 住在北京

# 忽略多个值
data = (1, 2, 3, 4, 5)
first, *middle, last = data # * 收集剩余的元素
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5

3. 函数返回多个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 函数可以返回元组,实现返回多个值
def get_user_info():
name = "Alice"
age = 25
city = "北京"
return name, age, city # 实际返回的是元组

# 接收返回值
name, age, city = get_user_info()
print(f"{name}, {age}岁, {city}")

# 也可以用一个变量接收整个元组
info = get_user_info()
print(info) # ('Alice', 25, '北京')
print(info[0]) # Alice

五、元组 vs 列表

主要区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 可变性不同
list_data = [1, 2, 3]
tuple_data = (1, 2, 3)

list_data[0] = 100 # ✅ 列表可以修改
# tuple_data[0] = 100 # ❌ 元组不能修改

list_data.append(4) # ✅ 列表可以添加
# tuple_data.append(4) # ❌ 元组不能添加

# 2. 性能差异
import timeit

# 创建列表和元组的时间对比
print(timeit.timeit('[1,2,3,4,5]')) # 约 0.05 秒
print(timeit.timeit('(1,2,3,4,5)')) # 约 0.01 秒(更快)

# 3. 作为字典的键
d1 = {[1,2]: "value"} # ❌ 列表不能作为键
d2 = {(1,2): "value"} # ✅ 元组可以作为键

六、元组的高级用法

1. 命名元组(namedtuple)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from collections import namedtuple

# 定义一个有名字的元组类型
Point = namedtuple('Point', ['x', 'y'])
Person = namedtuple('Person', 'name age city')

# 创建实例
p = Point(3, 5)
person = Person('Alice', 25, '北京')

# 可以通过属性访问,也可以通过索引访问
print(p.x, p.y) # 3 5
print(p[0], p[1]) # 3 5

print(person.name) # Alice
print(person[1]) # 25

# 比普通元组更可读
def get_data():
return Person('Bob', 30, '上海')

name, age, city = get_data()
print(f"{name} {age}{city}")

2. 元组作为记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 元组可以用来表示固定结构的数据
students = [
('Alice', 85, 'A'),
('Bob', 92, 'A+'),
('Charlie', 78, 'B+')
]

# 遍历时直接拆包
for name, score, grade in students:
print(f"{name}: {score}分, {grade}")

# 排序
students.sort(key=lambda x: x[1], reverse=True) # 按分数降序
print(students)

3. 元组生成式

1
2
3
4
5
6
7
8
9
10
11
# 注意:元组没有推导式,圆括号里的是生成器表达式
t = (x**2 for x in range(5))
print(t) # <generator object> 不是元组!

# 要创建元组需要用 tuple()
t = tuple(x**2 for x in range(5))
print(t) # (0, 1, 4, 9, 16)

# 或者用列表推导式转元组
t = tuple([x**2 for x in range(5)])
print(t) # (0, 1, 4, 9, 16)

七、实际应用场景

场景1:函数返回多个值会变成元组

1
2
3
4
5
6
7
def divide_and_remainder(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder # 返回元组

q, r = divide_and_remainder(10, 3)
print(f"商:{q}, 余数:{r}") # 商:3, 余数:1

场景2:遍历字典时候返回的是元组

1
2
3
4
5
6
7
8
9
10
d = {'a': 1, 'b': 2, 'c': 3}

# items() 返回的是元组
for key, value in d.items():
print(f"{key} -> {value}")

# 等价于
for item in d.items():
key, value = item # item 是元组
print(f"{key} -> {value}")

八、常见陷阱和注意事项

1. 包含可变对象的元组

1
2
3
4
5
6
7
# 元组不可变,但元组里的列表是可变的
t = (1, 2, [3, 4])
# t[0] = 100 # ❌ 不能修改元组元素
t[2][0] = 99 # ✅ 可以修改列表内容
print(t) # (1, 2, [99, 4])

# 因为元组存的是引用,引用不可变,但引用指向的对象可变

2. 单元素元组必须加逗号

1
2
3
4
5
6
# 常见错误
a = (1) # 这是整数1
b = (1,) # 这是元组(1,)

print(type(a)) # <class 'int'>
print(type(b)) # <class 'tuple'>

3. 元组连接的性能

1
2
3
4
5
6
7
8
9
10
# 频繁连接元组会创建新元组,性能较差
t = ()
for i in range(1000):
t = t + (i,) # 每次创建新元组,O(n²)

# 更好的做法:用列表收集,最后转元组
temp = []
for i in range(1000):
temp.append(i)
t = tuple(temp) # 一次转换,O(n)

总结

元组是 Python 中简单而强大的数据结构:

  • 不可变:安全、可哈希、适合作为字典键
  • 轻量:比列表更省内存,性能更好
  • 拆包:优雅的多变量赋值
  • 记录:适合表示固定结构的数据

记住:当你不需要修改数据时,优先考虑用元组!