一、引言:序列 —— 程序中的数据集装箱
在Python中,序列(Sequence)是一种重要的数据结构,它是一组有序元素的集合,每个元素可以通过索引(位置)来访问。列表(list)和元组(tuple)是Python中最常用、最基础的两种序列类型,它们几乎出现在每一个Python程序中。
无论是存储用户列表、处理数据集合,还是作为函数参数传递,列表和元组都扮演着不可或缺的角色。理解它们的异同、掌握它们的操作方法,是每个Python开发者必须练就的基本功。
本文将全面深入地讲解列表与元组,从创建、访问、修改、遍历,到高级技巧如列表推导式、切片、解包,以及它们之间的转换和性能比较。全文超过5000字,配有大量实用代码示例,帮助你彻底掌握这两种序列类型。
本文目标: 让你深入理解列表和元组的设计哲学,熟练运用它们解决实际问题,并能在恰当的场合选择正确的序列类型。
二、列表:灵活的多功能容器
列表是Python中最灵活、最常用的序列类型,它是可变的(mutable),即可以修改其内容,包括添加、删除、修改元素。
2.1 创建列表
列表使用方括号 [] 定义,元素之间用逗号分隔。可以包含任意类型的对象,甚至可以嵌套。
empty = [] numbers = [1, 2, 3, 4, 5] mixed = [1, "hello", 3.14, [2, 3]] # 嵌套列表 range_list = list(range(10)) # 通过 range() 创建
2.2 访问元素
使用索引(从0开始)访问单个元素,支持负索引(-1表示最后一个)。
fruits = ["apple", "banana", "cherry"] print(fruits[0]) # apple print(fruits[-1]) # cherry
2.3 切片操作
切片可以获取子列表,语法为 list[start:end:step]。
nums = [0, 1, 2, 3, 4, 5] print(nums[1:4]) # [1, 2, 3] print(nums[:3]) # [0, 1, 2] print(nums[::2]) # [0, 2, 4] print(nums[::-1]) # 反转 [5,4,3,2,1,0]
2.4 修改列表
列表是可变的,可以直接通过索引赋值修改元素。
fruits[1] = "blueberry" print(fruits) # ['apple', 'blueberry', 'cherry']
2.5 添加元素
append(x):在末尾添加一个元素。insert(i, x):在索引 i 处插入元素。extend(iterable):将另一个序列的所有元素追加到末尾。
fruits.append("orange")
fruits.insert(1, "grape")
fruits.extend(["mango", "kiwi"])
print(fruits) # ['apple', 'grape', 'blueberry', 'cherry', 'orange', 'mango', 'kiwi']
2.6 删除元素
pop(i):删除并返回索引 i 处的元素,默认删除最后一个。remove(x):删除第一个值为 x 的元素。del list[i]:通过 del 语句删除。clear():清空列表。
last = fruits.pop() # 删除并返回 'kiwi'
fruits.remove("grape") # 删除 'grape'
del fruits[0] # 删除第一个元素
print(fruits) # ['blueberry', 'cherry', 'orange', 'mango']
2.7 查找与统计
index(x):返回第一个 x 的索引,若不存在则抛出 ValueError。count(x):返回 x 出现的次数。in运算符:检查元素是否在列表中。
nums = [1, 2, 3, 2, 4] print(nums.index(2)) # 1 print(nums.count(2)) # 2 print(3 in nums) # True
2.8 排序与反转
sort(key=None, reverse=False):就地排序,返回 None。sorted(list):返回新排序列表,不改变原列表。reverse():就地反转。
nums = [5, 2, 8, 1] nums.sort() print(nums) # [1, 2, 5, 8] nums.reverse() print(nums) # [8, 5, 2, 1]
2.9 列表推导式
列表推导式提供了一种简洁的创建列表的方式,通常比循环更清晰、更快速。
squares = [x**2 for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] even = [x for x in range(20) if x % 2 == 0] # 过滤
还可以嵌套循环:
matrix = [[1,2,3], [4,5,6], [7,8,9]] flat = [num for row in matrix for num in row] # [1,2,3,4,5,6,7,8,9]
2.10 列表的复制
赋值(new = old)只是引用同一个列表。要创建副本,可以使用:
new = old.copy()或new = old[:](浅拷贝)copy.deepcopy(old)(深拷贝,处理嵌套列表)
小贴士: 列表可以容纳不同类型的元素,但实际项目中通常建议保持类型一致,以增强可读性和可维护性。
三、元组:不可变的序列
元组与列表非常相似,但关键区别在于元组是不可变的(immutable)。一旦创建,就无法修改其内容。元组使用圆括号 () 定义。
3.1 创建元组
empty = () single = (5,) # 注意逗号,否则会被视为整数 numbers = (1, 2, 3, 4) mixed = (1, "hello", 3.14) tuple_from_list = tuple([1,2,3])
3.2 访问元素
与列表一样,支持索引和切片。
t = (10, 20, 30, 40) print(t[1]) # 20 print(t[1:3]) # (20, 30)
3.3 元组的不可变性
尝试修改元组会引发 TypeError。
# t[0] = 99 # 错误!
但如果元组包含可变对象(如列表),可以修改该对象的内容,但元组的引用不变。
t = ([1,2], [3,4]) t[0].append(99) # 允许,t 变为 ([1,2,99], [3,4])
3.4 元组的方法
由于不可变,元组的方法较少,主要有 count() 和 index(),用法与列表相同。
3.5 元组的用途
- 保护数据:防止意外修改。
- 作为字典的键:因为可哈希(必须包含不可变元素)。
- 函数返回多个值:Python 函数可以返回元组,方便解包。
- 性能优势:元组比列表更轻量,占用内存更少,访问速度略快。
四、列表与元组的对比
| 特性 | 列表 (list) | 元组 (tuple) |
|---|---|---|
| 可变性 | 可变 (可增删改) | 不可变 |
| 语法 | [] |
() |
| 常用方法 | append, extend, insert, remove, pop, sort, reverse, clear | count, index |
| 内存占用 | 较大(需要额外空间支持修改) | 较小(更紧凑) |
| 速度 | 略慢 | 略快 |
| 适用场景 | 需要动态修改的数据集 | 固定数据、字典键、函数返回值 |
五、序列通用操作
列表和元组都属于序列,因此支持许多共同的操作。
- 拼接:
+连接两个序列。 - 重复:
*重复序列。 - 长度:
len()。 - 最值:
max(),min()。 - 求和:
sum()(仅数值)。 - 成员检查:
in和not in。 - 迭代:
for item in seq:。
a = [1, 2] b = [3, 4] c = a + b # [1,2,3,4] d = a * 3 # [1,2,1,2,1,2] print(len(c)) # 4 print(2 in a) # True
六、高级技巧:解包与星号表达式
Python 支持将序列解包为多个变量,这在处理元组或列表时非常方便。
point = (10, 20) x, y = point # x=10, y=20 # 星号 * 用于收集多余元素(Python 3.0+) first, *rest = [1, 2, 3, 4] # first=1, rest=[2,3,4] *head, last = [1, 2, 3, 4] # head=[1,2,3], last=4
星号表达式在函数调用时也用于解包参数:
args = [1, 2, 3] print(*args) # 相当于 print(1, 2, 3)
七、列表与元组的相互转换
使用 list() 和 tuple() 可以方便地转换。
t = (1, 2, 3) lst = list(t) # [1, 2, 3] t2 = tuple(lst) # (1, 2, 3)
八、性能与内存考量
由于元组不可变,它的结构更简单,因此创建元组比列表更快,占用内存也更少。在存储大量静态数据时,优先选择元组。对于需要频繁增删改的动态数据,列表是唯一选择。
此外,列表的 append() 和 pop() 操作在末尾是高效的(O(1)),但在中间插入或删除(O(n))会较慢。对于需要频繁在中间操作的情况,可以考虑使用 collections.deque。
九、常见陷阱与最佳实践
9.1 列表的浅拷贝问题
当列表包含可变对象时,使用 copy() 或切片创建的是浅拷贝,内部对象仍共享引用。
outer = [[1,2], [3,4]] inner = outer.copy() inner[0][0] = 99 print(outer) # [[99,2], [3,4]] —— 外部也被修改
需要使用 copy.deepcopy() 进行深拷贝。
9.2 元组作为字典键
元组必须包含不可变元素才能作为字典的键。如果包含列表,则不可哈希。
9.3 不要用列表作为函数默认参数
默认参数在函数定义时求值,如果使用空列表作为默认值,多次调用会共享同一个列表,导致意外的累积。
def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] —— 并非预期
正确做法是使用 None 作为默认值,内部创建新列表。
9.4 列表推导式 vs 循环
列表推导式通常比显式循环更快、更简洁,但过度复杂的推导式会降低可读性,应保持简洁。
9.5 使用 enumerate() 获取索引
在遍历列表时,如果需要同时获取索引和值,应使用 enumerate()。
for i, val in enumerate(fruits):
print(i, val)
十、实战示例
10.1 学生成绩管理
scores = [85, 92, 78, 90, 88]
# 计算平均分
average = sum(scores) / len(scores)
print(f"平均分: {average:.2f}")
# 筛选不及格(<60)
failed = [s for s in scores if s < 60]
print("不及格人数:", len(failed))
10.2 矩阵转置(使用列表推导式)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed) # [[1,4,7], [2,5,8], [3,6,9]]
10.3 元组作为坐标
points = [(10,20), (30,40), (50,60)]
for x, y in points:
print(f"x={x}, y={y}")
10.4 使用列表实现栈(后进先出)
stack = []
stack.append("a")
stack.append("b")
stack.append("c")
print(stack.pop()) # c
print(stack.pop()) # b
十一、总结与进一步学习
本文系统介绍了Python中的两种核心序列类型——列表和元组,涵盖创建、访问、修改、切片、推导式、解包、转换、性能对比以及常见陷阱。列表提供了强大的可变性和丰富的操作方法,适合动态数据处理;元组则以其不可变性和轻量级特性,在固定数据和保护数据方面有着独特优势。
熟练掌握列表和元组,是编写Python程序的基础。下一步,你可以学习其他序列类型如字符串、range,以及更高级的数据结构如字典、集合,并探索 collections 模块中的 deque、namedtuple 等工具。













