for i, module in enumerate(target_modules): lora_a_i = lora_a[i] # lora_a 可能比 target_modules 短
✅ 正确:先检查长度
for i, module in enumerate(target_modules): if i >= len(lora_a): logger.warning(f"lora_a has no entry for module {i}, skipping") continue lora_a_i = lora_a[i]
### 场景 2:API 响应 `choices` 为空(LiteLLM#30761)
```python
# ❌ 错误:假设 choices 永远非空
first_choice = response.choices[0]
# ✅ 正确:防御式访问
if not response.choices:
logger.warning("Empty choices in response, skipping")
return None
first_choice = response.choices[0]
真实触发条件:上游模型在流式响应的第一个 chunk 中发送 choices=[]——这发生在模型返回空内容、速率限制、或中间件过滤了所有候选时。
场景 3:sys.argv 无保护访问
# ❌ 错误:假设用户传了参数
filename = sys.argv[1] # python script.py → IndexError
# ✅ 正确
if len(sys.argv) < 2:
print("Usage: python script.py <filename>")
sys.exit(1)
filename = sys.argv[1]
场景 4:split() 后取固定下标
# ❌ 错误:假设 split 结果有 3 段
name, version, arch = package_string.split("-") # "nginx-1.24" → IndexError
# ✅ 正确
parts = package_string.split("-")
if len(parts) == 2:
name, version = parts
arch = "amd64" # 默认值
elif len(parts) == 3:
name, version, arch = parts
场景 5:迭代中删除元素导致索引漂移
# ❌ 错误:边遍历边删除
items = [1, 2, 3, 4, 5]
for i in range(len(items)):
if items[i] % 2 == 0:
del items[i] # 列表变短,后续 i 越界
# ✅ 正确:列表推导式新建
items = [x for x in items if x % 2 != 0]
排障流程
Step 1:确定哪个列表、哪个下标
Traceback 最后三行:
File "xxx.py", line N, in func
xxx[i]
IndexError: list index out of range
→ 列表名:xxx(可能是动态计算的结果)
→ 下标:i(看上一行的赋值或循环变量)
→ 检查 len(xxx) vs i
Step 2:在崩溃点前加防御日志
try:
value = some_list[index]
except IndexError:
logger.error(
f"IndexError: len(some_list)={len(some_list)}, "
f"index={index}, type={type(index)}"
)
raise
Step 3:用 pdb 或 breakpoint() 停住现场
import pdb; pdb.set_trace()
# 然后:
# (Pdb) len(lora_a)
# (Pdb) i
# (Pdb) type(i)
# (Pdb) lora_a[:10] # 看前 10 个元素
Step 4:修复原则
| 模式 | 修复 |
|---|---|
| 循环中下标越界 | if i < len(x): |
| 空列表取首个 | if x: first = x[0] |
| split 后段数不够 | if len(parts) >= N: |
| API 响应可能为空 | if response.choices: |
总结
| 层级 | 理解 |
|---|---|
| 初级 | 取列表元素时下标超出范围就抛 IndexError |
| 中级 | CPython 在 PyList_GetItem 中做 O(1) 边界检查,失败时设置 PyExc_IndexError。防御式编程的核心是永远不假设列表非空或下标有效 |
| 记忆锚点 | choices[0] 是无保护下标访问的经典反模式——写 choices[0] 之前,先问自己「如果 choices 是空的,这条代码会怎样?」 |
同类家族
| 报错 | 场景 | 本系列篇目 |
|---|---|---|
TypeError: 'str' object is not callable | 变量名覆盖内置函数 | 01 |
AttributeError: 'NoneType' object has no attribute | None 对象上调用方法 | 02 |
ImportError: cannot import name | 循环导入或模块不存在 | 03 |
KeyError: dict missing key | 字典键不存在 | 04 |
RecursionError: max recursion depth | 递归深度超限 | 05 |
ModuleNotFoundError: setuptools | 包安装环境混乱 | 06 |
JSONDecodeError | pickle 序列化进程池崩溃 | 07 |
IndexError: list index out of range | 无保护下标访问 | 08 ← 本文 |