下面是纯 Python 示例,字符串列表只是方便观察,不涉及真实模型调用。
def prepare(history):
messages = history
request = {"messages": messages}
messages.append("查询订单")
messages = ["取消订单"]
return request
def send(request):
try:
raise TimeoutError("请求超时")
except TimeoutError:
request["messages"].append("超时记录")
raise
history = ["系统规则"]
request = prepare(history)
try:
reply = send(request)
history.append("请求完成")
except TimeoutError:
history.append("外层已处理")
假设执行前没有定义过 reply。请先不运行,只回答三点:
- 最终
history和request["messages"]分别是什么? 为什么有没有"取消订单"?
答:history是"系统规则",request["messages"]是"系统规则","查询订单";因为messages指向的是另一个列表 与request不同
reply是否获得了值? "请求完成"会不会加入列表?
答:没有 不会
send()中的raise把异常交给了哪里? 为什么最后能执行"外层已处理"?
答:外层函数 try 与 except 因为符合except TimeoutError的情况 舅执行了 "外层已处理"
答:第 2、3 点答对了;第 1 点对"取消订单"的判断正确,但把共享列表的变化漏掉了。 我们按执行顺序把它串起来。
1. 最终两个列表是什么?
history和request["messages"]最终都指向同一个列表:
["系统规则", "查询订单", "超时记录", "外层已处理"]关键在于这两行:
messages = history request = {"messages": messages}这里没有复制列表。此时三个位置指向同一个对象:
history ─────────────┐ messages ────────────┼──→ ["系统规则"] request["messages"] ─┘接下来每一步的变化如下:
执行语句 原列表的内容 messages.append("查询订单")["系统规则", "查询订单"]messages = ["取消订单"]不变,只让局部变量 messages指向新列表request["messages"].append("超时记录")["系统规则", "查询订单", "超时记录"]history.append("外层已处理")["系统规则", "查询订单", "超时记录", "外层已处理"]你说的"
messages指向另一个列表"确实成立,但它发生在重新赋值之后,而且没有解除history与request["messages"]之间的共享关系。重新赋值后的关系是:
history ─────────────┐ ├──→ ["系统规则", "查询订单"] request["messages"] ─┘ messages ───────────────→ ["取消订单"]所以"取消订单"不会出现在最终结果里。
2.
reply和"请求完成":你的判断正确。
reply = send(request)Python 要先执行右侧调用,正常返回后才会给左侧赋值。
send()抛出了异常,因此:
reply没有获得值;按题设,它仍未定义。- 执行流程跳到匹配的
except,跳过history.append("请求完成")。3.
raise的去向:你的理解正确。这里不带参数的
raise会重新抛出当前正在处理的异常:
send() 抛出 TimeoutError ↓ send() 内部 except 捕获它 ↓ 追加"超时记录" ↓ raise 重新抛出 ↓ 调用处外层 except TimeoutError 捕获 ↓ 追加"外层已处理"一个措辞修正:这里是"调用处的外层
try/except",示例中这段代码并没有放在另一个函数里。这次复核已经确认你理解了异常传播、赋值未完成和异常后的语句跳过 。剩下要巩固的是:重新绑定一个变量,不会改变其他变量对原对象的引用;异常也不会自动撤销此前对列表的修改。