在Python编程中,列表(list)和元组(tuple)是最基础也最常用的两种序列类型。二者都用于存储有序元素集合,但特性截然不同:列表可变而元组不可变。掌握它们的设计哲学和操作方法,是写出高效Python代码的关键。本文从创建、访问、修改、遍历到高级技巧(如切片、推导式、解包、深浅拷贝),再到性能对比和常见陷阱,带你系统梳理列表与元组的应用。
---
一、列表:灵活的可变序列
列表通过方括号 [] 定义,元素用逗号分隔,支持任意嵌套。
- empty = []
- numbers = [1, 2, 3, 4, 5]
- mixed = [1, "hello", 3.14, [2, 3]]
- range_list = list(range(10))
复制代码
1. 索引与切片
索引从0开始,支持负索引。切片语法 list[start:end:step] 返回子列表。
- fruits = ["apple", "banana", "cherry"]
- print(fruits[0]) # apple
- print(fruits[-1]) # cherry
- nums = [0, 1, 2, 3, 4, 5]
- print(nums[1:4]) # [1, 2, 3]
- print(nums[::2]) # [0, 2, 4]
- print(nums[::-1]) # [5,4,3,2,1,0]
复制代码
2. 修改元素
直接通过索引赋值修改。
- fruits[1] = "blueberry"
- print(fruits) # ['apple', 'blueberry', 'cherry']
复制代码
3. 添加元素
append(x) 追加到末尾;insert(i, x) 在指定位置插入;extend(iterable) 将可迭代对象的所有元素追加。
- fruits.append("orange")
- fruits.insert(1, "grape")
- fruits.extend(["mango", "kiwi"])
- print(fruits) # ['apple', 'grape', 'blueberry', 'cherry', 'orange', 'mango', 'kiwi']
复制代码
4. 删除元素
pop(i) 弹出并返回索引i处的元素(默认最后一个);remove(x) 删除第一个值为x的元素;del 按索引删除;clear() 清空列表。
- last = fruits.pop() # 删除并返回 'kiwi'
- fruits.remove("grape") # 删除 'grape'
- del fruits[0] # 删除第一个元素
- print(fruits) # ['blueberry', 'cherry', 'orange', 'mango']
复制代码
5. 查找与统计
index(x) 返回第一个x的索引(不存在则抛出ValueError);count(x) 统计次数;in 检查是否存在。
- nums = [1, 2, 3, 2, 4]
- print(nums.index(2)) # 1
- print(nums.count(2)) # 2
- print(3 in nums) # True
复制代码
6. 排序与反转
sort() 就地排序;sorted() 返回新列表;reverse() 就地反转。
- nums = [5, 2, 8, 1]
- nums.sort()
- print(nums) # [1, 2, 5, 8]
- nums.reverse()
- print(nums) # [8, 5, 2, 1]
复制代码
7. 列表推导式
一种简洁创建列表的方式,通常比循环更快。
- squares = [x**2 for x in range(10)] # [0, 1, 4, 9, ...]
- 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]
复制代码
8. 列表的复制
赋值(new = old)只是引用,不创建副本。浅拷贝可用 copy() 或 old[:];深拷贝需 import copy 后使用 copy.deepcopy()。
---
二、元组:不可变的序列
元组用圆括号 () 定义,一旦创建不可修改。
- empty = ()
- single = (5,) # 注意逗号,否则是整数
- numbers = (1, 2, 3, 4)
- mixed = (1, "hello", 3.14)
- tuple_from_list = tuple([1,2,3])
复制代码
索引和切片方式与列表相同。
- t = (10, 20, 30, 40)
- print(t[1]) # 20
- print(t[1:3]) # (20, 30)
复制代码
由于不可变,元组的方法只有 count() 和 index()。若元组中包含可变对象(如列表),该对象内容仍可修改,但元组本身的元素引用不变。
元组的典型用途:
- 保护数据不被意外修改
- 用作字典的键(元组中所有元素必须可哈希)
- 函数返回多个值(自动打包为元组)
- 性能优于列表:创建更快,占用内存更少
---
三、列表与元组的对比
| 特性 | 列表 | 元组 |
|------|------|------|
| 可变性 | 可变(可增删改) | 不可变 |
| 语法 | [] | () |
| 常用方法 | append, extend, insert, remove, pop, sort, reverse, clear | count, index |
| 内存占用 | 较大(支持动态修改) | 较小 |
| 速度 | 略慢 | 略快 |
| 适用场景 | 需要动态修改的数据集 | 固定数据、字典键、函数返回值 |
---
四、序列通用操作
列表和元组都是序列,支持:
- 拼接 +、重复 *
- len()、max()、min()、sum()(仅数值)
- in / not in 成员检查
- for 循环迭代
- 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
复制代码
---
五、高级技巧:解包与星号表达式
将序列解包到多个变量:
- point = (10, 20)
- x, y = point # x=10, y=20
- 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)
复制代码
---
七、常见陷阱与最佳实践
1. 列表的浅拷贝问题
只需用 copy() 或切片做的是浅拷贝,内部可变对象仍共享引用。
- outer = [[1,2], [3,4]]
- inner = outer.copy()
- inner[0][0] = 99
- print(outer) # [[99,2], [3,4]] 外部也被改
复制代码
解决:使用 copy.deepcopy()。
2. 元组作为字典键
元组中若包含列表等不可哈希对象,则不能作为键。
3. 不要用列表作为函数默认参数
默认参数在函数定义时仅求值一次,多次调用会共用同一列表,导致数据意外累积。
- def add_item(item, lst=[]):
- lst.append(item)
- return lst
- print(add_item(1)) # [1]
- print(add_item(2)) # [1, 2] 并非预期
复制代码
正确做法:默认值设为 None,函数内部创建新列表。
4. 列表推导式 vs 循环
推导式通常更快更简洁,但连续复杂嵌套会降低可读性,应保持适度。
5. 使用 enumerate() 获取索引
遍历时需同时获取索引和元素,用 enumerate()。
- for i, val in enumerate(fruits):
- print(i, val)
复制代码
---
八、实战示例
1. 学生成绩管理
- scores = [85, 92, 78, 90, 88]
- average = sum(scores) / len(scores)
- print(f"平均分: {average:.2f}")
- failed = [s for s in scores if s < 60]
- print("不及格人数:", len(failed))
复制代码
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]]
复制代码
3. 元组作为坐标
- points = [(10,20), (30,40), (50,60)]
- for x, y in points:
- print(f"x={x}, y={y}")
复制代码
4. 用列表实现栈(后进先出)
- stack = []
- stack.append("a")
- stack.append("b")
- stack.append("c")
- print(stack.pop()) # c
- print(stack.pop()) # b
复制代码
---
列表和元组是Python数据处理的两大基石。列表可变、方法丰富,适合动态数据;元组不可变、轻量、可哈希,适合固定数据和保护性场景。理解它们的区别,并能根据需求灵活选择,是高效编程的重要技能。在此基础上,可以进一步学习其他序列类型(字符串、range)以及 collections 模块中的 deque、namedtuple 等工具,以应对更复杂的数据结构需求。 |