Exception handling · 异常处理
| English | 中文 | Pinyin · 拼音 |
|---|---|---|
| exception/ekˈsepʃn/ | 异常 | yì cháng |
| exception handling/ekˈsepʃn ˈhændlɪŋ/ | 异常处理 | yì cháng chǔ lǐ |
| raise/reɪz/ | 抛出 | pāo chū |
The check that cannot be written
- A program opens a file. Before opening it, you could check that the file exists, that it is readable, that the disk is present. Suppose every check passes.
- Between the last check and the open, a user pulls out the USB stick. The file existed when you asked and does not exist when you open it, and no amount of checking in advance could have closed that gap.
- Some errors are simply not preventable, because the world changes between the test and the action. What a program can do is respond to them when they happen.
- This lesson is the exception 异常, the TRY structure that handles one, and where in a program an exception should be caught.
写不出来的那个检查
- 一个程序要打开一个文件。打开之前,你可以检查文件是否存在、是否可读、磁盘是否在位。假设每项检查都通过了。
- 在最后一次检查和真正打开之间,用户把 U 盘拔了。你问的时候文件在,你打开的时候它不在,而事先再多的检查也堵不上这道缝。
- 有些错误根本无法预防,因为在检验和动作之间世界变了。程序能做的是在它们发生时作出响应。
- 这一课讲异常(exception)、处理异常的 TRY 结构,以及异常应该在程序的哪个层次被捕获。
What an exception is and why handle it
- An exception is an error that occurs during execution: division by zero, a file not found, invalid input, a network that has gone away.
- Exception handling 异常处理 lets the program detect the error and respond in a controlled way, instead of crashing and losing the user's work.
- Real programs face errors that cannot be prevented in advance, as the hook shows. And without exceptions, every single operation would need its own
IFcheck around it, burying the actual algorithm in error tests. - The third benefit is the one the exam most often asks for: it separates the normal flow from the error handling, so the main path reads cleanly.
什么是异常,为什么要处理
- 异常是执行期间发生的错误:除以零、文件找不到、无效输入、网络断了。
- 异常处理(exception handling)让程序检测到错误并以受控的方式响应,而不是崩溃并丢掉用户的工作。
- 真实程序面对的错误无法事先预防,正如开头那个例子。而且没有异常的话,每一个操作都要用自己的
IF检查包起来,把真正的算法埋在错误检验里。 - 第三个好处是考试最常问的:它把正常流程与错误处理分开,让主路径读起来干净。
An exception is: · 一个异常是:
Exceptions are run-time problems (divide by zero, file not found) that handling lets you respond to gracefully. · 异常是运行时的问题(除以零、文件未找到),处理让你优雅地响应它们。
Why use exception handling rather than checking for every error in advance? Select all · 所有 that apply. · 为什么用异常处理而不是事先检查每一种错误?选出所有适用的。
The benefits are correctness and clarity. Speed is not one of them; handling adds a little overhead, which is worth paying. · 好处是正确性和清晰性。速度不在其中;处理会带来一点开销,而这个开销值得付。
The TRY structure
- The TRY block holds the code that might fail. If it runs without error, the EXCEPT blocks are skipped entirely.
- If an exception occurs, execution jumps immediately to the first matching EXCEPT block. The rest of the TRY block does not run.
- A FINALLY block runs whether or not an exception happened, which makes it the right place for cleanup such as closing a file.
One path when all is well, another when it is not, and one that always runs
TRY 结构
TRY
OPENFILE "data.txt" FOR READ
READFILE "data.txt", line
CLOSEFILE "data.txt"
EXCEPT FileNotFound
OUTPUT "Sorry, the file does not exist."
EXCEPT ReadError
OUTPUT "Sorry, there was an error reading the file."
FINALLY
OUTPUT "Finished attempting to read."
ENDTRY
- TRY 块装可能失败的代码。如果它无错运行完,各个 EXCEPT 块被完全跳过。
- 如果发生异常,执行立即跳到第一个匹配的 EXCEPT 块。TRY 块的其余部分不会运行。
- FINALLY 块无论是否发生异常都会运行,这让它成为关闭文件之类清理工作的正确位置。

一切顺利时一条路,不顺利时另一条,还有一条总会走
How exception handling flows · 异常处理如何流动
Step through what happens when code fails. The exception jumps out of the normal flow to a handler, FINALLY cleans up either way, and the program carries on instead of crashing. · 逐步走过当代码失败时发生什么。异常跳出正常流程到一个处理器,FINALLY 无论如何都清理,而程序继续而不是崩溃。
Match each exception-handling keyword to its job. · 把每个异常处理关键字与它的工作配对。
TRY guards the risky code, EXCEPT catches, FINALLY cleans up either way, RAISE throws an error to be caught. · TRY 守卫有风险的代码,EXCEPT 捕获,FINALLY 无论如何都清理,RAISE 抛出一个要被捕获的错误。
A FINALLY block: · 一个 FINALLY 块:
FINALLY always runs, making it ideal for cleanup such as closing files. · FINALLY 总是运行,使它对像关闭文件这样的清理理想。
The block that runs whether or not an exception occurred, making it right for closing files, is ____. · 无论是否发生异常都会运行、因而适合用来关闭文件的那个块是 ____。
Cleanup must happen on both paths. Putting CLOSEFILE only in the TRY block means it is skipped exactly when an error left the file open. · 清理在两条路径上都必须发生。只把 CLOSEFILE 放在 TRY 块里,恰恰会在出错、文件还开着的时候被跳过。
Worked example: trace the flow
- In the code above, the file does not exist. State exactly what is output and what is skipped.
OPENFILEraises FileNotFound, so execution leaves the TRY block immediately: theREADFILEandCLOSEFILElines never run.- The
FileNotFoundEXCEPT block runs, outputting "Sorry, the file does not exist." TheReadErrorblock does not run, because only the first matching handler is used. - The FINALLY block runs, outputting "Finished attempting to read."
- The mark most often lost is noticing that the rest of the TRY block is abandoned.
例题:追踪流程
- 在上面的代码中,文件不存在。准确说出输出了什么、跳过了什么。
OPENFILE引发 FileNotFound,所以执行立即离开 TRY 块:READFILE和CLOSEFILE两行永远不会运行。FileNotFound的 EXCEPT 块运行,输出 "Sorry, the file does not exist."。ReadError块不会运行,因为只使用第一个匹配的处理器。- FINALLY 块运行,输出 "Finished attempting to read."。
- 最常被丢掉的一分,是注意到 TRY 块的其余部分被放弃了。
The file in the TRY block does not exist. Put what happens in order. · TRY 块中的文件不存在。把发生的事按顺序排列。
Only the first matching handler runs, and FINALLY runs either way. The skipped remainder of the TRY block is the mark most often missed. · 只有第一个匹配的处理器运行,而 FINALLY 无论如何都运行。被跳过的 TRY 块其余部分是最常被漏掉的一分。
Raising an exception
- A subroutine that detects a problem it cannot sensibly deal with can raise 抛出 an exception, passing responsibility to whoever called it:
- This is the right design when the subroutine knows something is wrong but not what should be done about it. A division routine knows the divisor is zero; only the caller knows whether to ask the user again, use a default, or abandon the calculation.
抛出异常
- 一个子程序若发现了自己无法妥善处理的问题,可以抛出(raise)一个异常,把责任交给调用它的人:
IF b = 0 THEN RAISE DivideByZero
- 当子程序知道出了问题、却不知道该怎么办时,这就是正确的设计。除法例程知道除数是零;只有调用者知道该重新问用户、用一个默认值,还是放弃这次计算。
A subroutine uses RAISE to: · 一个子程序用 RAISE 来:
RAISE throws an exception up to the caller, which can catch it with EXCEPT. · RAISE 把一个异常抛给调用者,调用者能用 EXCEPT 捕获它。
Why would a division subroutine RAISE an exception instead of handling a zero divisor itself? · 除法子程序为什么要 RAISE 一个异常,而不是自己处理除数为零?
Ask the user again, use a default, or abandon the calculation: only the calling code has the context to choose. · 重新问用户、用默认值,还是放弃这次计算:只有调用方的代码有足以选择的上下文。
Where to handle it
- Handle an exception close to where it occurs when the response is simple and local: print a message, use a default, ask the user to try again.
- Handle it higher up when the decision belongs to a larger part of the program: whether to abandon a whole transaction, roll back a change, or tell the user the operation failed.
- The rule of thumb: catch it at the level that has enough information to decide what to do, not at the level that first notices.
在哪里处理
- 当响应简单且局部时,在错误发生的地方附近处理它:打印一条消息、用一个默认值、请用户重试。
- 当决定属于程序中更大的一部分时,在更高层处理它:是否放弃整笔事务、回滚一次改动,或告诉用户操作失败了。
- 经验法则:在有足够信息决定该做什么的那一层捕获它,而不是在最先察觉的那一层。
Worked example: what not to do
- A student writes
EXCEPT: (do nothing)around a whole program so it never crashes. Explain why this is poor practice. - The exception is swallowed: the program continues as though nothing went wrong, so it carries on with missing or invalid data and produces wrong results instead of an obvious failure.
- A silent failure is harder to diagnose than a crash, because there is no message and no indication of where it happened.
- Catching every exception in one place also means the specific error type is lost, so no sensible response can be chosen. A handler should catch a specific exception and actually respond to it.
例题:不该做什么
- 一名学生在整个程序外面写了
EXCEPT:(什么也不做),让它永不崩溃。解释这为什么是糟糕的做法。 - 异常被吞掉了:程序像什么都没出错一样继续,于是带着缺失或无效的数据往下走,产出错误的结果,而不是一次明显的失败。
- 静默的失败比崩溃更难诊断,因为没有消息,也没有任何关于它发生在哪里的线索。
- 在一个地方捕获所有异常还意味着具体的错误类型丢失了,于是无法选择合理的响应。处理器应该捕获特定的异常并真的对它作出响应。
Silently "swallowing" an exception (catching it but doing nothing) hides real errors and makes debugging hard — you should handle it or at least log it. · 默默地“吞掉”一个异常(捕获它但什么都不做)隐藏真正的错误并使调试困难——你应该处理它或至少记录它。
An empty handler hides the very problems you need to find; always respond or record the error. · 一个空的处理器隐藏你需要找到的那些问题;总是响应或记录错误。
Marks that slip away
- When an exception occurs, the rest of the TRY block is skipped. Say so when tracing.
- Only the first matching EXCEPT runs, not all of them.
- FINALLY always runs, error or no error, which is what makes it right for closing files.
- Do not catch an exception and do nothing. A swallowed error is worse than a crash, because the program continues with bad data.
容易丢掉的分
- 发生异常时,TRY 块的其余部分被跳过。追踪时要说出来。
- 只有第一个匹配的 EXCEPT 运行,不是全部。
- FINALLY 总会运行,有没有出错都一样,这正是它适合关闭文件的原因。
- 不要捕获异常却什么都不做。被吞掉的错误比崩溃更糟,因为程序会带着坏数据继续跑。
You've got it
- an exception is a run-time error; handling it lets a program respond instead of crashing, covers errors that cannot be prevented in advance, and separates normal code from error handling
- TRY holds the risky code; on an error the rest of it is skipped and the first matching EXCEPT runs; FINALLY runs either way, so cleanup belongs there
- a subroutine that cannot decide the response should RAISE the exception for its caller
- handle it close by for a simple local response, higher up when the decision needs a wider view; never catch it and do nothing
你掌握了
- 异常是运行时错误;处理它让程序能响应而不是崩溃,覆盖那些无法事先预防的错误,并把正常代码与错误处理分开
- TRY 装有风险的代码;出错时其余部分被跳过,第一个匹配的 EXCEPT 运行;FINALLY 两种情况下都运行,所以清理工作放在那里
- 无法决定响应的子程序应该把异常 RAISE 给它的调用者
- 简单的局部响应就近处理,需要更大视野的决定在更高层处理;绝不要捕获了却什么都不做