列表(List):创建、索引、切片、常用方法
一句话概述
列表(List)是 Python 中最常用、最灵活的数据结构——一个有序的、可变的容器,可以存放任意类型和任意数量的元素。你可以把它想象成一个「可擦写的购物清单」:随时添加新项目、删除不要的、修改已有的、调整顺序。列表支持整数索引(从 0 开始)快速访问任意位置的元素,支持切片提取子列表,还自带一整套内置方法(append、insert、remove、sort 等)让操作变得极其方便。掌握列表,是学习 Python 数据处理的第一个里程碑,也是通往 AI 数据处理、特征工程、批量运算的必经之路。
💡 核心要点:①列表是可变的、有序的、可重复的元素容器,用方括号[]创建 ②索引从 0 开始,负数索引从 -1 倒着数 ③切片语法[start:end:step]提取子列表,左闭右开 ④append/pop/insert/remove/sort 等常用方法原地修改列表 ⑤列表推导式是 Pythonic 的精髓,一行代码完成过滤和变换
教学与演示
一、列表的创建——从空盒子到百宝箱
是什么:列表(list)是 Python 内置的可变序列类型,用一对方括号 [] 创建,元素之间用逗号分隔。列表可以包含任意类型的元素——数字、字符串、布尔值、甚至另一个列表(嵌套列表)。同一个列表中可以混合不同类型的数据(虽然通常不推荐这样做)。
大白话 把列表想象成火车的一节节车厢——每节车厢(元素)都有编号(索引),你可以随时加挂新车厢、摘掉旧车厢、换掉某节车厢里的货物。Python 的列表比火车灵活得多——每节车厢可以装完全不同的货物(整数、字符串、另一个列表),而且没有长度限制。
为什么:在实际编程中,很少只处理单个数据——你需要存储一组学生成绩、一批商品价格、一段文本的所有单词。列表就是专门用来"管一批数据"的容器。它的可变性让你能动态增删数据(不像元组创建后就定死了),它的有序性让你能按位置快速访问(不像字典需要先知道键)。在 AI 中,原始数据在喂给模型之前,几乎都要经过"转成列表 → 处理 → 转成 numpy 数组"这个流程。
大白话 没有列表,你处理 100 个数字就得定义 100 个变量(a1, a2, a3... a100)——光是给变量起名字就疯了。有了列表,一行scores = [85, 92, 78, ...]搞定,然后一行sum(scores) / len(scores)算出平均分。这就是"批量处理"的魅力。
怎么做:
import numpy as np
# ====== 1. 创建列表的四种方式 ======
# 方式一:直接用方括号(最常用)
fruits = ["苹果", "香蕉", "橘子", "西瓜"] # 字符串列表
print("水果列表:", fruits) # ['苹果', '香蕉', '橘子', '西瓜']
# 方式二:list() 构造函数,从其他可迭代对象转换
chars = list("hello") # 把字符串拆成字符列表
print("字符列表:", chars) # ['h', 'e', 'l', 'l', 'o']
# 方式三:list(range()) 生成等差数列
nums = list(range(1, 11)) # 生成 1 到 10 的整数列表
print("数字列表:", nums) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 方式四:列表乘法——重复已有列表
zeros = [0] * 5 # 创建 5 个 0 的列表
print("零列表:", zeros) # [0, 0, 0, 0, 0]
# ====== 2. 列表可以混合不同类型(但不推荐在正规场合) ======
mixed = [42, "hello", True, 3.14, None] # 整数、字符串、布尔、浮点数、空值
print("混合列表:", mixed)
print("混合列表长度:", len(mixed)) # 5 — len() 返回元素个数
# ====== 3. 嵌套列表(二维列表) ======
# 二维列表就像矩阵——行和列
matrix = [
[1, 2, 3], # 第 0 行
[4, 5, 6], # 第 1 行
[7, 8, 9] # 第 2 行
]
print("二维矩阵:", matrix)
print("第1行第2列:", matrix[1][2]) # 6 — 先取第1行[4,5,6],再取第2个元素
# ====== 4. 空列表——先占个位,后面再填 ======
empty = [] # 空列表,长度为 0
print("空列表:", empty, "长度:", len(empty)) # [] 长度: 0
# AI 中常见的模式:先创建空列表,循环中逐个添加数据
data = [] # 初始化空列表
for i in range(3):
data.append(np.random.random()) # 逐个添加随机数
print("收集的数据:", [round(x, 4) for x in data])什么用:在 AI 中,列表的创建场景极其丰富。读取 CSV 文件的每一行是列表,分词(tokenization)后的结果是列表,训练过程中每个 epoch 的损失值记录在列表中。比如收集训练损失曲线:losses = [],每个 epoch 结束后 losses.append(current_loss),最后用 plt.plot(losses) 画图。嵌套列表更是图像处理的基础——一张灰度图本质上就是一个二维列表,每个元素是像素值。
二、索引——按位置精确取元素
是什么:索引让你通过位置编号访问列表中的单个元素。Python 使用零基索引——第一个元素位置是 0,第二个是 1,以此类推。负数索引从末尾反向计数——-1 是最后一个元素,-2 是倒数第二个。
大白话 列表的索引就像楼层的编号。但在 Python 这座楼里,一楼是 0 楼(不是 1 楼)。地下室的编号方式也特别:-1 楼是地下室第一层(倒数第一个),-2 楼是地下室第二层(倒数第二个)。这个约定一开始可能别扭,但习惯了会发现负数索引非常方便——你永远知道 lst[-1] 就是最后一个元素。
为什么:索引是访问列表元素的最基本操作。几乎所有涉及列表的算法——查找、遍历、修改——都离不开索引。索引 O(1) 的时间复杂度(无论列表多长,访问任意位置的时间都一样)是列表最大的性能优势。理解零基索引也是理解 Python 切片行为的前提。
大白话 零基索引不是 Python 的发明——几乎所有主流编程语言(C、C++、Java、JavaScript)都用零基索引。这是因为在底层,索引本质上是从内存起始地址的"偏移量"——第 0 个元素在偏移 0 处,第 1 个在偏移 1 处,非常自然。
怎么做:
import numpy as np
# ====== 1. 正索引——从左往右数,从 0 开始 ======
# 创建一个 AI 中常见的样本标签列表
labels = ["猫", "狗", "鸟", "鱼", "兔"]
# 正索引: 0 1 2 3 4
# 负索引: -5 -4 -3 -2 -1
print("=== 正索引 ===")
print("labels[0] =", labels[0]) # '猫' — 第一个元素
print("labels[1] =", labels[1]) # '狗' — 第二个元素
print("labels[4] =", labels[4]) # '兔' — 最后一个(正索引)
# ====== 2. 负索引——从右往左数,从 -1 开始 ======
print("\n=== 负索引 ===")
print("labels[-1] =", labels[-1]) # '兔' — 倒数第一个
print("labels[-2] =", labels[-2]) # '鱼' — 倒数第二个
print("labels[-5] =", labels[-5]) # '猫' — 倒数第五个(即第一个)
# ====== 3. 索引越界——超出范围会报错 ======
try:
print(labels[10]) # 只有 5 个元素,索引 10 越界
except IndexError as e:
print(f"索引越界错误: {e}") # list index out of range
# ====== 4. 通过索引修改元素(列表是可变的!) ======
print("\n=== 修改元素 ===")
print("修改前:", labels)
labels[0] = "老虎" # 把第 0 个元素从 "猫" 改成 "老虎"
labels[-1] = "乌龟" # 把最后一个元素从 "兔" 改成 "乌龟"
print("修改后:", labels) # ['老虎', '狗', '鸟', '鱼', '乌龟']
# ====== 5. 在 AI 中:用索引批量更新预测结果 ======
# 模拟:模型预测概率,根据阈值修改分类标签
probs = [0.2, 0.8, 0.5, 0.9, 0.3] # 模型对各样本的预测概率
preds = ["未知"] * 5 # 初始化预测结果,全部为"未知"
threshold = 0.5 # 分类阈值
for i in range(len(probs)): # 遍历每个索引
if probs[i] >= threshold: # 概率大于阈值
preds[i] = "正类" # 通过索引修改对应位置的标签
else:
preds[i] = "负类"
print("\n预测结果:", preds) # ['负类', '正类', '负类', '正类', '负类']什么用:在 AI 数据处理中,索引操作无处不在。批处理中通过索引访问 batch[i] 获取第 i 个样本;迁移学习中通过索引替换分类层:model.layers[-1] = new_layer;one-hot 编码中通过索引找到最大值位置:np.argmax(predictions[i]) 返回预测类别。索引还是理解多维数组(tensor)操作的基础——tensor[0, :, :] 取第 0 个通道,本质上也是索引。
三、切片——一次取出一个子列表
是什么:切片(Slice)让你用 [start:end:step] 语法一次提取列表的一个连续(或等间隔)子集。切片返回的是新列表(浅拷贝),不修改原列表。三个参数中 start 包含(默认 0),end 不包含(左闭右开,默认末尾),step 是步长(默认 1,可为负数)。
大白话 如果说索引是"拿一件东西",切片就是"端一盘东西"。lst[1:4]的意思是:从 1 号位置开始,端到 4 号位置之前(即端走 1、2、3 号,4 号不端)。lst[::2]的意思是:从头到尾,隔一个拿一个。lst[::-1]的意思是:从尾到头,倒着拿——结果是反转后的列表。
为什么:切片是 Python 最优雅的特性之一,它把"提取子序列"这个常见需求浓缩成了一个简洁的语法。在其他语言中,提取子序列可能需要写循环或者调用复杂的 subList 方法;Python 中一行切片就搞定了。在 AI 中,数据划分(训练集取前 80%)、滑动窗口、序列截断等操作都大量依赖切片。
大白话 没有切片的世界:你想取列表的前 5 个元素,得写一个 for 循环,i 从 0 到 4,逐个 append 到新列表——四行代码。有切片:lst[:5]——四个字符。Python 的设计哲学就是"让简单的事情简单做"。
怎么做:
import numpy as np
# ====== 1. 基本切片:list[start:end] 左闭右开 ======
# 模拟 AI 训练中的 10 个 epoch 的损失值
losses = [0.95, 0.78, 0.65, 0.55, 0.48, 0.42, 0.38, 0.35, 0.33, 0.31]
print("完整损失:", losses)
# start:end 左闭右开
print("losses[0:3] =", losses[0:3]) # [0.95, 0.78, 0.65] — 索引 0,1,2
print("losses[2:5] =", losses[2:5]) # [0.65, 0.55, 0.48] — 索引 2,3,4
# ====== 2. 省略 start 或 end ======
print("\n=== 省略参数 ===")
print("losses[:4] =", losses[:4]) # [0.95, 0.78, 0.65, 0.55] — 从头到索引3
print("losses[6:] =", losses[6:]) # [0.38, 0.35, 0.33, 0.31] — 从索引6到末尾
print("losses[:] =", losses[:]) # 完整拷贝(浅拷贝),等于 losses.copy()
# ====== 3. 负索引切片 ======
print("\n=== 负索引切片 ===")
print("losses[-3:] =", losses[-3:]) # [0.35, 0.33, 0.31] — 最后 3 个
print("losses[:-2] =", losses[:-2]) # 除了最后 2 个的所有元素
# ====== 4. 步长 step——隔几个取一个 ======
print("\n=== 步长切片 ===")
print("losses[::2] =", losses[::2]) # 每隔一个取:索引 0,2,4,6,8
print("losses[1::2] =", losses[1::2]) # 从索引1开始,隔一个取:索引 1,3,5,7,9
# ====== 5. 负步长——反转列表(非常实用!) ======
print("\n=== 反转 ===")
print("losses[::-1] =", losses[::-1]) # [0.31, 0.33, ...] — 完全反转!
# ====== 6. AI 实际场景:按比例切分训练集和验证集 ======
data = list(range(100)) # 模拟 100 条数据(0-99)
split_point = int(len(data) * 0.8) # 80% 处切分
train_data = data[:split_point] # 前 80 条作为训练集
val_data = data[split_point:] # 后 20 条作为验证集
print(f"\n总数据: {len(data)} 条")
print(f"训练集: {len(train_data)} 条 ({len(train_data)/len(data)*100:.0f}%)")
print(f"验证集: {len(val_data)} 条 ({len(val_data)/len(data)*100:.0f}%)")
print(f"训练集前5条: {train_data[:5]}") # 0-4
print(f"验证集前5条: {val_data[:5]}") # 80-84什么用:切片在 AI 中极为实用。数据划分:train, val, test = data[:800], data[800:900], data[900:];序列处理:input_seq = sequence[i:i+window_size] 创建滑动窗口;数据增强中反转图像:augmented = image[:, ::-1](水平翻转);注意力机制中的 mask 操作:mask[i, :j] = 1。切片语法和 numpy/PyTorch 的数组切片完全一致——学一次,到处用。
四、常用方法——列表自带的工具箱
是什么:列表作为 Python 的内置类型,自带一套丰富的方法(method),用于增删改查排序等常见操作。这些方法大多是"原地操作"(in-place)——直接修改原列表,不返回新列表。掌握这些方法,操作列表就像使用一个功能齐全的工具箱。
大白话 列表自带的方法就像瑞士军刀上的各种工具——append是在末尾加一个(像在队伍最后加一个人),insert是在指定位置插入(像插队),pop是弹出一个(像抽出一张扑克牌),remove是按值删除(像通缉某个特定元素),sort是排序(像按身高排队)。注意这些方法大多数直接修改原列表,和字符串的方法(返回新字符串)习惯不一样。
为什么:列表的强大不仅在于它能存数据,更在于这些内置方法让数据操作极其高效。A 语言可能需要写 5 行代码才能删除某个元素,Python 一行 lst.remove(x) 就搞定。而且这些方法是用 C 语言实现的,比你自己写循环快得多。理解每个方法的时间复杂度(append 是 O(1),insert 是 O(n))也能帮你写出更高效的代码。
大白话 就像你不需要知道电视机内部电路就能换台一样,你不需要知道列表底层怎么移动元素就能用lst.sort()排序。但你最好知道:在列表开头插入元素(insert(0, x))很慢——因为后面所有元素都得往后挪——这就像在排好的队伍最前面插一个人,后面所有人都得退一步。
怎么做:
import numpy as np
# ====== 1. 添加元素:append、insert、extend ======
nums = [1, 2, 3]
print("原始列表:", nums)
# append(x):在末尾添加一个元素(O(1),最快!)
nums.append(4)
print("append(4) 后:", nums) # [1, 2, 3, 4]
# insert(i, x):在指定位置 i 插入元素(O(n),后面元素都要后移)
nums.insert(1, 99) # 在索引 1 处插入 99
print("insert(1, 99) 后:", nums) # [1, 99, 2, 3, 4]
# extend(iterable):把另一个可迭代对象的所有元素追加到末尾
nums.extend([10, 20, 30])
print("extend([10,20,30]) 后:", nums) # [1, 99, 2, 3, 4, 10, 20, 30]
# ====== 2. 删除元素:pop、remove、clear ======
# pop(i):删除并返回索引 i 处的元素(默认删最后一个,O(1))
popped = nums.pop() # 默认 pop(-1),删除最后一个
print(f"\npop() 删除了: {popped},剩余: {nums}")
popped = nums.pop(1) # 删除索引 1 处的元素(99)
print(f"pop(1) 删除了: {popped},剩余: {nums}")
# remove(x):删除第一个值为 x 的元素(O(n))
nums.remove(10) # 删除第一个出现的 10
print(f"remove(10) 后: {nums}") # 10 被删了
# clear():清空列表
temp = [1, 2, 3]
temp.clear()
print(f"clear() 后: {temp}") # []
# ====== 3. 查找与统计:index、count ======
fruits = ["苹果", "香蕉", "橘子", "香蕉", "西瓜"]
print(f"\n水果列表: {fruits}")
# index(x):返回第一个值为 x 的索引(找不到抛 ValueError)
print(f"'香蕉' 的索引: {fruits.index('香蕉')}") # 1 — 第一个香蕉在索引1
print(f"'橘子' 的索引: {fruits.index('橘子')}") # 2
# count(x):统计某元素出现的次数
print(f"'香蕉' 出现了: {fruits.count('香蕉')} 次") # 2
# ====== 4. 排序与反转:sort、reverse ======
scores = [85, 92, 78, 65, 95, 70]
print(f"\n原始分数: {scores}")
# sort():原地排序(默认升序)
scores.sort()
print(f"升序排序: {scores}") # [65, 70, 78, 85, 92, 95]
# sort(reverse=True):降序排序
scores.sort(reverse=True)
print(f"降序排序: {scores}") # [95, 92, 85, 78, 70, 65]
# reverse():原地反转
scores.reverse()
print(f"反转后: {scores}") # [65, 70, 78, 85, 92, 95]
# ====== 5. 复制:copy() ======
original = [1, 2, 3]
shallow = original.copy() # 浅拷贝——创建新列表
shallow[0] = 999
print(f"\noriginal: {original}") # [1, 2, 3] — 原列表不受影响
print(f"shallow: {shallow}") # [999, 2, 3]
# ====== 6. AI 实战:动态收集训练指标 ======
epoch_losses = [] # 空列表,记录每个 epoch 的损失
for epoch in range(1, 6): # 模拟 5 个 epoch
# 模拟损失逐步下降
loss = np.exp(-0.5 * epoch) + np.random.normal(0, 0.02)
epoch_losses.append(round(loss, 4)) # 把每轮损失追加到列表中
print(f"\n训练损失记录: {epoch_losses}")
print(f"最低损失: {min(epoch_losses)}") # min() 内置函数找最小值
print(f"损失收敛趋势: 从 {epoch_losses[0]} 降到 {epoch_losses[-1]}")什么用:在 AI 开发中,列表方法的使用频率极高。append 收集训练指标、构建 batch;sort 按置信度排序预测结果;pop 从候选列表中取出最优选项;reverse 反转序列(如 beam search 中的路径回溯)。数据预处理中,remove 清洗异常值,extend 合并多个数据源,index 查找标签对应的类别索引。这些方法组合起来,就是数据处理流水线的基础构件。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| list | 可变、有序的元素容器 | 存储数据集、训练指标、预测结果 | tuple、array |
| 索引 | 通过位置编号访问元素 | batch 索引、层索引、分类序号 | 切片、偏移量 |
| 切片 | 提取连续子序列 | 数据划分(train/val/test)、滑动窗口 | 索引、步长 |
| append | 末尾追加元素,O(1) | 收集 epoch 损失、动态构建 batch | extend、insert |
| pop | 删除并返回元素 | 从候选列表取出最优项 | remove、del |
| sort | 原地排序 | 按置信度排序、Top-K 选择 | sorted()、reverse |
| 可变性 | 创建后可以修改元素 | 动态更新模型参数缓存 | 不可变、引用 |
| 嵌套列表 | 列表的元素还是列表 | 图像矩阵、批量数据、注意力矩阵 | 二维数组、张量 |
重点答疑
Q1: 列表的 append 和 extend 有什么区别?
append(x) 把 x 当作一个整体添加到列表末尾。如果 x 是列表比如 [4, 5],那整个 [4, 5] 作为一个元素加进去(嵌套)。extend(iterable) 则把可迭代对象的每个元素逐个追加进去。举例:a = [1, 2]; a.append([3, 4]) 得到 [1, 2, [3, 4]](嵌套);而 a = [1, 2]; a.extend([3, 4]) 得到 [1, 2, 3, 4](展开)。简单记忆:append = 加一个,extend = 加一堆。
Q2: list.sort() 和 sorted(list) 有什么区别?
list.sort() 是列表的方法,原地修改原列表,返回 None——这是 Python 的惯例:修改原对象的方法返回 None。sorted(list) 是内置函数,返回一个新的排序后的列表,原列表不变。所以 b = a.sort() 会让 b 变成 None(常见错误!),正确做法是直接 a.sort() 后用 a,或者 b = sorted(a)。此外 sorted() 可以用于任何可迭代对象(元组、字典、集合),而 .sort() 只有列表才有。
Q3: 为什么 a = b 修改 a 会导致 b 也变了?什么是浅拷贝?
a = b 不是拷贝列表——它只是让变量 a 和 b 指向同一个列表对象。修改 a 的元素实际上修改了它们共同指向的那个列表,所以 b "也变了"。要创建独立的副本,用 a = b.copy() 或 a = b[:]。但注意这些都是"浅拷贝"——如果列表里有嵌套列表,内层列表还是共享的。完全独立的深拷贝需要 import copy; a = copy.deepcopy(b)。
Q4: 列表的索引为什么从 0 开始而不是从 1 开始?
这是计算机科学的传统,源自底层内存模型。索引本质上是"元素距起始地址的偏移量"——第一个元素在偏移 0 处(base + 0 * element_size),第二个在偏移 1 处(base + 1 * element_size)。几乎所有主流语言都采用零基索引(C、C++、Java、JavaScript、Go、Rust 等),少数语言如 MATLAB、R、Lua 从 1 开始,但它们更多面向数学/统计领域。学 Python 时记住:索引就是"前面跳过几个元素"。
Q5: 如何在遍历列表时安全地删除元素?
直接在 for item in lst: 循环中删除元素会导致跳过或索引错乱(因为列表长度在变)。正确做法有三种:①倒序遍历索引:for i in range(len(lst)-1, -1, -1): if 条件: del lst[i];②使用列表推导式创建新列表:lst = [x for x in lst if 条件](最 Pythonic);③遍历副本:for item in lst[:]: if 条件: lst.remove(item)。推荐方式②,代码最清晰,也是最常用做法。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| list | /lɪst/ | 列表;可变、有序的元素集合 |
| index | /ˈɪndeks/ | 索引;通过整数位置访问元素 |
| slice | /slaɪs/ | 切片;提取序列的子集 |
| append | /əˈpend/ | 追加;在列表末尾添加元素 |
| mutable | /ˈmjuːtəbl/ | 可变的;创建后可以修改内容 |
| sequence | /ˈsiːkwəns/ | 序列;有顺序的数据结构 |
| nested | /ˈnestɪd/ | 嵌套的;列表里面包含列表 |
| iterate | /ˈɪtəreɪt/ | 迭代;逐个遍历元素 |
面试练习
Q1 [单选] 以下哪个不是合法的列表创建方式?
- A.
lst = [1, 2, 3] - B.
lst = list("abc") - C.
lst = list(range(5)) - D.
lst = list[1, 2, 3]
解答:D 错误——list是类名不是函数时应该用list()调用,不能用方括号直接跟类名。A 是字面量创建,B 和 C 是构造函数创建。
Q2 [单选] nums = [10, 20, 30, 40, 50],nums[1:4] 的结果是?
- A.
[10, 20, 30] - B.
[20, 30, 40] - C.
[20, 30, 40, 50] - D.
[10, 20, 30, 40]
解答:切片 [1:4] 左闭右开,取索引 1、2、3 对应的元素,即 20、30、40。索引 4(50)不包含。
Q3 [单选] lst = [1, 2, 3]; lst.append([4, 5]); print(len(lst)) 输出什么?
- A. 3
- B. 5
- C. 4
- D. 报错
解答:append([4, 5])把[4, 5]作为一个整体追加,列表变成[1, 2, 3, [4, 5]],长度是 4,不是 5。如果要用 5 个元素,应该用extend。
Q4 [多选] 以下哪些操作会修改原列表?
- A.
lst.append(4) - B.
lst.sort() - C.
lst.reverse() - D.
lst[:]
解答:A、B、C 都是原地修改。D(切片 lst[:])返回新列表,原列表不变。
Q5 [单选] fruits = ["苹果", "香蕉", "橘子"]; print(fruits[-2]) 输出什么?
- A. 苹果
- B. 香蕉
- C. 橘子
- D. 报错
解答:负索引从 -1 开始倒着数:-1=橘子,-2=香蕉,-3=苹果。所以 fruits[-2] 是香蕉。
Q6 [多选] 关于 list.pop() 说法正确的是?
- A. 默认删除并返回最后一个元素
- B.
pop(0)删除第一个元素 - C.
pop()不会修改原列表 - D. 对空列表调用
pop()会报 IndexError
解答:A 正确(默认 pop(-1))。B 正确(指定索引删除)。C 错误(pop 是原地操作,会修改原列表)。D 正确(空列表没有元素可 pop)。
Q7 [单选] 如何正确复制一个列表 l1,使得修改 l2 不影响 l1?
- A.
l2 = l1 - B.
l2 = l1.copy() - C.
l2 = l1.append([]) - D.
l2 = l1 * 1
解答:B 正确,.copy()创建浅拷贝。A 只是别名指向同一对象。C 语法错误。D(l1 * 1)虽然也创建新列表,但不如.copy()语义清晰。更常用的写法还有l2 = l1[:]。
Q8 [多选] 以下代码中,a 的最终值是什么?a = [3, 1, 4, 1, 5]; a.sort(); a.pop()
- A.
[1, 1, 3, 4]— 排序后弹出了最大的 5 - B.
[3, 1, 4, 1] - C.
[1, 1, 3, 4, 5] - D.
[5, 4, 3, 1, 1]
解答:A 正确。sort()后 a 变成[1, 1, 3, 4, 5],pop()删除最后一个 5,最终[1, 1, 3, 4]。注意 sort() 返回 None 而不是排序后的列表。
Q9 [单选] 以下哪个操作的时间复杂度最高(最慢)?
- A.
lst.append(x) - B.
lst[-1] - C.
lst.insert(0, x) - D.
lst.pop()
解答:C 最慢(O(n))——在索引 0 插入元素会导致后面所有元素后移。append和pop()是 O(1)(均摊),索引访问也是 O(1)。在大列表中频繁在开头插入/删除会导致严重性能问题。
Q10 [多选] 对于 lst = [1, 2, 3, 2, 4, 2],以下操作正确的是?
- A.
lst.count(2)返回 3 - B.
lst.index(2)返回 1 - C.
lst.remove(2)删除所有值为 2 的元素 - D.
lst.index(2, 2)从索引 2 开始找 2,返回 3
解答:A 正确(2 出现了 3 次)。B 正确(第一个 2 在索引 1)。C 错误——remove只删除第一个匹配的元素,要删除所有需用循环或列表推导式。D 正确——index(x, start)从指定位置开始查找。