使用 list 和 tuple
列表(list)和元组(tuple)都是有顺序的序列,可以保存多个值。列表适合需要增删改的数据;元组适合结构固定、不希望被修改的数据。两者都支持索引、切片、遍历和许多序列操作。
创建序列
numbers = [10, 20, 30]
colors = ["red", "green", "blue"]
point = (10, 20)
empty_list = []
empty_tuple = ()
列表使用方括号,元组使用圆括号。元组的括号有时可以省略,但逗号不能省略:
coordinates = 10, 20
single = (42,)
not_tuple = (42)
print(type(coordinates)) # tuple
print(type(single)) # tuple
print(type(not_tuple)) # int
列表和元组可以嵌套,也可以保存不同类型的对象;实际项目中通常让元素含义一致:
matrix = [[1, 2], [3, 4]]
record = ("小林", 18, True)
索引、切片和长度
索引从 0 开始,负数从末尾开始:
languages = ["Python", "Go", "Dart"]
print(languages[0]) # Python
print(languages[-1]) # Dart
print(len(languages)) # 3
切片格式与字符串相同,结果总是一个新序列:
languages = ["Python", "Go", "Dart", "Rust"]
print(languages[1:3]) # ['Go', 'Dart']
print(languages[:2]) # ['Python', 'Go']
print(languages[::2]) # ['Python', 'Dart']
print(languages[::-1]) # ['Rust', 'Dart', 'Go', 'Python']
列表切片返回列表,元组切片返回元组。访问不存在的索引会抛出 IndexError。
修改列表
列表是可变对象,可以按索引替换元素:
scores = [80, 90, 75]
scores[2] = 85
print(scores) # [80, 90, 85]
也可以通过切片一次替换一段内容:
scores[0:2] = [82, 93, 88]
print(scores) # [82, 93, 88, 85]
右侧元素数量不必与被替换片段相同,因此切片赋值还能插入或删除元素:
numbers = [1, 4]
numbers[1:1] = [2, 3] # 插入
del numbers[0:2] # 删除
print(numbers) # [3, 4]
元组不可修改:
point = (10, 20)
# point[0] = 5 # TypeError
列表增删改查
添加元素
items = ["书"]
items.append("笔") # 在末尾添加一个元素
items.extend(["本子", "尺子"]) # 添加多个元素
items.insert(1, "橡皮") # 在指定位置插入
print(items)
append(value) 把整个 value 当作一个元素;extend(iterable) 会逐个添加可迭代对象中的元素:
left = [1, 2]
left.append([3, 4])
print(left) # [1, 2, [3, 4]]
right = [1, 2]
right.extend([3, 4])
print(right) # [1, 2, 3, 4]
删除元素
items = ["书", "笔", "本子", "笔"]
last = items.pop() # 删除并返回末尾元素
first = items.pop(0) # 删除并返回第一个元素
items.remove("笔") # 删除第一次出现的指定值
del items[0] # 按索引删除,不返回值
items.clear() # 清空列表
remove() 找不到目标时抛出 ValueError;删除前可用 if value in items 判断。频繁 pop(0) 的效率较低,队列场景应考虑 collections.deque。
查找和排序
numbers = [3, 1, 2, 1]
print(2 in numbers) # True
print(numbers.index(1)) # 1,第一次出现的位置
print(numbers.count(1)) # 2
numbers.sort() # 原地升序排序
print(numbers) # [1, 1, 2, 3]
numbers.reverse() # 原地反转
sort() 和 reverse() 返回 None,不要写成 sorted_numbers = numbers.sort()。需要保留原列表时,使用内置函数 sorted():
scores = [80, 95, 70]
ordered = sorted(scores, reverse=True)
print(scores) # [80, 95, 70]
print(ordered) # [95, 80, 70]
可以通过 key 指定排序依据:
students = [("小林", 88), ("小周", 95), ("小王", 76)]
by_score = sorted(students, key=lambda student: student[1], reverse=True)
print(by_score)
遍历序列
最常见的遍历方式是直接取得元素:
for language in ["Python", "Go", "Dart"]:
print(language)
需要索引时使用 enumerate(),不要手动维护计数器:
languages = ["Python", "Go", "Dart"]
for index, language in enumerate(languages, start=1):
print(f"{index}. {language}")
同时遍历多个序列可以使用 zip():
names = ["小林", "小周"]
scores = [88, 95]
for name, score in zip(names, scores):
print(f"{name}:{score}")
zip() 默认以较短序列为准。需要检查长度是否一致时,应提前验证,或使用 zip(..., strict=True)(Python 3.10+)。
列表推导式
列表推导式用一行表达“遍历并生成列表”:
squares = [number * number for number in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
even_squares = [number * number for number in range(10) if number % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
表达式过长或包含多层条件时,普通 for 循环通常更易读。推导式中的变量只在推导式自己的作用域中使用,不要依赖其在外部存在。
元组解包
解包把序列中的元素一次赋给多个变量:
point = (10, 20)
x, y = point
print(x, y)
left, right = "左边", "右边"
left, right = right, left
使用星号接收剩余元素:
first, *middle, last = [1, 2, 3, 4]
print(first, middle, last) # 1 [2, 3] 4
左右两侧数量不匹配会抛出 ValueError。解包常用于函数返回多个相关结果:
def min_max(values):
return min(values), max(values)
lowest, highest = min_max([3, 1, 8])
选择 list 还是 tuple
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 待办事项、购物车 | list | 元素需要增删改 |
| 坐标、RGB 颜色、日期三元组 | tuple | 结构固定,强调不可变 |
| 需要作为字典键的组合值 | tuple | 元组在元素可哈希时可作为键 |
| 只读配置或函数返回的固定记录 | tuple | 防止调用方意外修改 |
元组本身不可变,但如果它包含列表,列表仍可修改:
data = ("tags", ["python"])
data[1].append("beginner")
print(data) # ('tags', ['python', 'beginner'])
不可变是逐层讨论的属性,不能把“包含可变对象的元组”当作完全深度不可变。
复制和引用
赋值不会复制列表:
original = [1, 2]
alias = original
alias.append(3)
print(original) # [1, 2, 3]
需要独立的浅复制时,可以使用 copy()、切片或 list():
original = [1, 2]
copied = original.copy()
copied.append(3)
print(original) # [1, 2]
嵌套列表的浅复制仍会共享内层对象,需要时使用 copy.deepcopy(),或按业务结构明确复制规则。
常见错误
numbers = [0] * 3对数字等不可变值没问题;但rows = [[0] * 3] * 3会让三行引用同一个内层列表。应使用[[0] * 3 for _ in range(3)]。- 遍历列表时直接删除元素会跳过部分元素。可以先用列表推导式生成新列表,或倒序删除。
sort()是原地操作,sorted()返回新列表;根据是否需要保留原顺序选择。- 访问列表时不要假设输入一定非空,必要时先判断
if items:。
练习
- 创建一个包含 1 到 20 的列表,筛选出能被 3 整除的数字。
- 使用
enumerate()输出一个课程列表的编号和名称。 - 将两个列表
names和scores用zip()组成“姓名—成绩”元组列表,并按成绩降序排列。 - 编写函数,返回列表中的最小值、最大值和平均值,并用解包接收结果。
- 观察嵌套列表的浅复制行为,再使用列表推导式创建互不共享的二维列表。
小结
list有序且可变,tuple有序且不可变;两者都支持索引、切片和遍历。append()添加一个元素,extend()添加多个元素,pop()删除并返回元素,remove()按值删除。sort()原地排序,sorted()返回新列表;key可指定排序依据。enumerate()提供索引,zip()并行遍历,列表推导式适合简洁的数据转换。- 元组解包让多值返回和变量交换更清晰;星号可以接收剩余元素。
- 赋值是共享引用,复制嵌套结构时要区分浅复制和深复制。