为什么需要 try...except?
在编程中,错误是不可避免的,有些错误是由于程序员编写代码时的疏忽造成的(比如语法错误),但更多错误是在程序运行时,由于外部因素或意外情况导致的(比如用户输入了非数字内容、要读取的文件不存在、网络连接中断等)。

如果没有 try...except,当这些错误发生时,程序会立即崩溃,并显示一个 Traceback 错误信息,这在实际应用中是不可接受的,因为:
- 用户体验差:用户看到一堆看不懂的错误代码,而不是友好的提示。
- 程序不健壮:程序无法应对意外情况,非常脆弱。
- 资源无法释放:如果程序在打开文件或网络连接后崩溃,这些资源可能没有被正确关闭。
try...except 机制就是为了优雅地处理这些运行时错误,让程序在遇到问题时不会直接崩溃,而是可以执行预设的“挽救”操作,然后继续运行或优雅地退出。
try...except 的基本语法
try...except 的基本结构非常直观:
try:
# 尝试执行的代码块
# 你可能会预见到某些代码可能会引发错误
...
except ExceptionType as e:
# try 块中发生了 ExceptionType 类型的异常,
# 则执行这个代码块
# e 是捕获到的异常对象
...
工作流程:

- 执行
try块:Python 解释器会尝试执行try语句块中的代码。 - 检查错误:
try块中的代码没有发生任何错误,程序会跳过所有except块,继续执行try...except结构后面的代码。try块中的代码发生了错误,Python 会立即停止执行try块中的剩余代码,然后检查这个错误是否匹配某个except块。
- 处理错误:
- 如果找到了一个匹配的
except块(即except后面指定的异常类型与发生的错误类型一致),程序就会执行该except块中的代码。 - 执行完
except块后,程序会继续执行try...except结构后面的代码,就好像什么都没发生过一样。 - 如果没有找到匹配的
except块,异常会继续向上层传递,如果最终没有被捕获,程序还是会崩溃。
- 如果找到了一个匹配的
try...except 的核心用法
1 基本用法:捕获特定异常
这是最常见的用法,针对可能发生的特定错误类型进行捕获。
示例:处理 ZeroDivisionError(除零错误)
try:
a = 10
b = 0
result = a / b
print(f"计算结果是: {result}")
except ZeroDivisionError:
print("错误:不能将一个数除以零!")
print("程序继续执行...")
输出:
错误:不能将一个数除以零!
程序继续执行...
如果没有 try...except,程序会在 result = a / b 这里崩溃,但现在,它捕获了 ZeroDivisionError,打印了友好的提示信息,然后继续执行。
2 捕获多个异常
你可以在一个 try 语句后跟多个 except 块,来处理不同类型的错误。
示例:处理 ValueError(值错误)和 ZeroDivisionError
try:
# 尝试从用户输入获取一个数字
num_str = input("请输入一个数字作为除数: ")
divisor = int(num_str)
result = 100 / divisor
print(f"100 除以 {divisor} 的结果是: {result}")
except ValueError:
# 如果用户输入的不是数字,int() 会引发 ValueError
print("错误:您输入的不是有效的整数!")
except ZeroDivisionError:
# 如果用户输入的是 0,会引发 ZeroDivisionError
print("错误:除数不能为零!")
print("程序结束。")
运行情况 1 (输入 "abc"):
请输入一个数字作为除数: abc
错误:您输入的不是有效的整数!
程序结束。
运行情况 2 (输入 "0"):
请输入一个数字作为除数: 0
错误:除数不能为零!
程序结束。
3 捕获所有异常(不推荐,但需了解)
你可以使用 except: (后面不跟任何异常类型) 来捕获所有类型的异常。这通常被认为是不好的编程实践,因为它会捕获像 KeyboardInterrupt (Ctrl+C) 或 SystemExit 这样的系统退出信号,导致你无法正常终止程序。
try:
# 一些可能出错的代码
risky_operation()
except:
# 会捕获所有异常
print("发生了一个未知错误!")
更好的做法是捕获 Exception 基类:
Exception 是几乎所有用户程序应该捕获的异常的基类,它不会捕获系统级的退出信号。
try:
risky_operation()
except Exception as e:
# 捕获所有非系统退出的异常
print(f"发生了一个错误: {e}")
4 as 关键字:获取异常对象
使用 as e 可以将捕获到的异常对象赋值给变量 e,这样你就可以打印出更详细的错误信息,或者根据不同的错误类型执行不同的逻辑。
try:
with open("不存在的文件.txt", "r") as f:
content = f.read()
except FileNotFoundError as e:
# e 是 FileNotFoundError 类型的对象
print(f"捕获到文件未找到的异常: {e}")
print(f"异常的参数是: {e.args}")
# 输出:
# 捕获到文件未找到的异常: [Errno 2] No such file or directory: '不存在的文件.txt'
# 异常的参数是: (2, 'No such file or directory: \'不存在的文件.txt\'')
try...except 的高级结构
try...except 还可以结合 finally 和 raise 使用,使其更加强大。
1 finally 子句
finally 块中的代码无论是否发生异常都会被执行,它通常用于执行“清理”工作,比如关闭文件、释放网络连接、释放锁等。
语法:
try:
# 可能出错的代码
...
except SomeError:
# 处理错误
...
finally:
# 无论是否出错,都会执行的代码
...
示例:确保文件被关闭
file = None
try:
file = open("my_file.txt", "w")
file.write("Hello, Python!")
# 如果在写入时磁盘满了,会引发 IOError
except IOError as e:
print(f"写入文件时出错: {e}")
finally:
# 无论 try 块是否成功,finally 都会确保文件被关闭
if file:
file.close()
print("文件已关闭。")
2 raise 语句:手动抛出异常
raise 语句用于手动触发一个异常,这在你需要根据程序逻辑判断某个条件不满足,从而中断程序流程时非常有用。
语法:
raise ExceptionType("错误信息")
示例:
def set_age(age):
if not isinstance(age, int):
raise TypeError("年龄必须是整数")
if age < 0 or age > 150:
raise ValueError("年龄必须在 0 到 150 之间")
print(f"年龄设置为: {age}")
try:
set_age(-5)
except (TypeError, ValueError) as e:
print(f"无效的年龄输入: {e}")
# 输出:
# 无效的年龄输入: 年龄必须在 0 到 150 之间
最佳实践
-
“请求原谅比许可更容易”(EAFP):这是 Python 的哲学,意思是“先尝试去做,如果失败了再处理它”(
try...except),这与其他一些语言的“先检查再做”(LBYL, Look Before You Leap)风格相反,EAFP 通常更优雅、更高效。- EAFP (Pythonic):
try: d = my_dict['key'] except KeyError: # 处理 key 不存在的情况 ... - LBYL (非 Pythonic):
if 'key' in my_dict: d = my_dict['key'] else: # 处理 key 不存在的情况 ...
- EAFP (Pythonic):
-
只捕获你期望的异常:永远不要使用裸露的
except:,要尽可能精确地捕获你预料到的异常类型,这可以防止你意外地掩盖了真正的问题。 -
在
except块中做最少的事:except块应该尽量简短,只负责记录错误、通知用户或进行恢复,不要在except块中写复杂的业务逻辑。 -
使用
finally进行资源清理:任何需要被确保执行的清理代码(如关闭文件、释放锁)都应该放在finally块中。 -
提供有意义的错误信息:当你
raise一个异常或打印错误信息时,确保信息清晰、准确,能帮助开发者快速定位问题。
| 关键字 | 作用 | 何时使用 |
|---|---|---|
try |
包裹可能引发异常的代码块。 | 当你认为某段代码可能会因为运行时错误而失败时。 |
except |
捕获并处理特定类型的异常。 | 当你想在错误发生时执行替代逻辑,而不是让程序崩溃时。 |
finally |
定义无论是否发生异常都会执行的代码块。 | 用于执行清理操作,如关闭文件、释放资源。 |
raise |
手动抛出一个异常。 | 当程序状态不符合预期,需要中断流程并通知调用者时。 |
掌握 try...except 是编写健壮、可靠 Python 程序的必备技能,它让你的代码能够优雅地面对不完美和意外。
