杰瑞科技汇

Python的try exception如何高效捕获异常?

为什么需要 try...except

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

Python的try exception如何高效捕获异常?-图1
(图片来源网络,侵删)

如果没有 try...except,当这些错误发生时,程序会立即崩溃,并显示一个 Traceback 错误信息,这在实际应用中是不可接受的,因为:

  • 用户体验差:用户看到一堆看不懂的错误代码,而不是友好的提示。
  • 程序不健壮:程序无法应对意外情况,非常脆弱。
  • 资源无法释放:如果程序在打开文件或网络连接后崩溃,这些资源可能没有被正确关闭。

try...except 机制就是为了优雅地处理这些运行时错误,让程序在遇到问题时不会直接崩溃,而是可以执行预设的“挽救”操作,然后继续运行或优雅地退出。


try...except 的基本语法

try...except 的基本结构非常直观:

try:
    # 尝试执行的代码块
    # 你可能会预见到某些代码可能会引发错误
    ...
except ExceptionType as e:
    # try 块中发生了 ExceptionType 类型的异常,
    # 则执行这个代码块
    # e 是捕获到的异常对象
    ...

工作流程:

Python的try exception如何高效捕获异常?-图2
(图片来源网络,侵删)
  1. 执行 try:Python 解释器会尝试执行 try 语句块中的代码。
  2. 检查错误
    • try 块中的代码没有发生任何错误,程序会跳过所有 except 块,继续执行 try...except 结构后面的代码。
    • try 块中的代码发生了错误,Python 会立即停止执行 try 块中的剩余代码,然后检查这个错误是否匹配某个 except 块。
  3. 处理错误
    • 如果找到了一个匹配的 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 还可以结合 finallyraise 使用,使其更加强大。

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 之间

最佳实践

  1. “请求原谅比许可更容易”(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 不存在的情况
          ...
  2. 只捕获你期望的异常:永远不要使用裸露的 except:,要尽可能精确地捕获你预料到的异常类型,这可以防止你意外地掩盖了真正的问题。

  3. except 块中做最少的事except 块应该尽量简短,只负责记录错误、通知用户或进行恢复,不要在 except 块中写复杂的业务逻辑。

  4. 使用 finally 进行资源清理:任何需要被确保执行的清理代码(如关闭文件、释放锁)都应该放在 finally 块中。

  5. 提供有意义的错误信息:当你 raise 一个异常或打印错误信息时,确保信息清晰、准确,能帮助开发者快速定位问题。

关键字 作用 何时使用
try 包裹可能引发异常的代码块。 当你认为某段代码可能会因为运行时错误而失败时。
except 捕获并处理特定类型的异常。 当你想在错误发生时执行替代逻辑,而不是让程序崩溃时。
finally 定义无论是否发生异常都会执行的代码块。 用于执行清理操作,如关闭文件、释放资源。
raise 手动抛出一个异常。 当程序状态不符合预期,需要中断流程并通知调用者时。

掌握 try...except 是编写健壮、可靠 Python 程序的必备技能,它让你的代码能够优雅地面对不完美和意外。

分享:
扫描分享到社交APP
上一篇
下一篇