跳到主要内容

使用 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. 创建一个包含 1 到 20 的列表,筛选出能被 3 整除的数字。
  2. 使用 enumerate() 输出一个课程列表的编号和名称。
  3. 将两个列表 names 和 scores 用 zip() 组成“姓名—成绩”元组列表,并按成绩降序排列。
  4. 编写函数,返回列表中的最小值、最大值和平均值,并用解包接收结果。
  5. 观察嵌套列表的浅复制行为,再使用列表推导式创建互不共享的二维列表。

小结​

  • list 有序且可变,tuple 有序且不可变;两者都支持索引、切片和遍历。
  • append() 添加一个元素,extend() 添加多个元素,pop() 删除并返回元素,remove() 按值删除。
  • sort() 原地排序,sorted() 返回新列表;key 可指定排序依据。
  • enumerate() 提供索引,zip() 并行遍历,列表推导式适合简洁的数据转换。
  • 元组解包让多值返回和变量交换更清晰;星号可以接收剩余元素。
  • 赋值是共享引用,复制嵌套结构时要区分浅复制和深复制。