Python 异常处理: EAFP 和真正什么是异常?

153 阅读3分钟

Python 异常处理的 EAFP (Easier to Ask for Forgiveness than Permission) 原则鼓励在发生错误时使用 try-except 块来处理错误,而不是在每个步骤都进行显式的检查。然而,也有人认为异常应该只在真正异常的情况下才抛出,例如网络连接失败或文件无法打开的情况。

huake_00183_.jpg 对于像下面这种情况,在优先级队列上不断弹出和压入元素,直到只剩下一个元素,使用 EAFP 原则处理异常是否合理呢?

import heapq

pq = a_list[:]
heapq.heapify(pq)

while True:
    min1 = heapq.heappop(pq)
    try:
        min2 = heapq.heappop(pq)
    except IndexError:
        break
    else:
        heapq.heappush(pq, min1 + min2)

# do something with min1

在这个例子中,异常只会在循环的 len(a_list) 次迭代中抛出一次,但它并不是真正的异常,因为我们知道它最终会发生。这种处理方式可以避免我们多次检查 a_list 是否为空,但它可能比使用显式条件的代码更难读懂。

那么,对于这种非异常的程序逻辑,使用异常处理的共识是什么呢?

解决方案

对于是否在非异常情况下使用异常处理,不同的开发人员有不同的看法,主要有以下几种观点:

  • 异常应该只在真正异常的情况下抛出。例如,网络连接失败或文件无法打开的情况。这是传统的异常处理思想,认为异常是程序运行中意外发生的事件,应该尽量避免。

  • 异常处理是 Python 中正常的流程控制工具。Python 中的异常处理非常灵活,可以用来处理各种不同的情况。即使是像循环结束这样的情况,也可以使用异常处理来处理。这使得 Python 代码更加简洁和易读。

  • 异常处理在Python中是昂贵的,应该谨慎使用。在Python中,抛出一个异常是一个相对昂贵的操作,因此应该谨慎使用异常处理。尽量避免在每次迭代中都使用异常处理,而是在序列的末尾使用异常处理。

  • 使用异常处理可以使代码更加健壮。异常处理可以帮助我们捕获代码中的错误,并对这些错误进行处理,从而使代码更加健壮。

最终,是否在非异常情况下使用异常处理取决于具体情况。如果异常处理可以使代码更加简洁、易读或健壮,那么就可以使用异常处理。否则,应该避免使用异常处理。

代码例子

以下是一些代码例子,展示了如何使用异常处理来处理非异常的情况:

# 使用异常处理来处理循环结束的情况

import heapq

pq = a_list[:]
heapq.heapify(pq)

try:
    while True:
        min1 = heapq.heappop(pq)
        min2 = heapq.heappop(pq)
        heapq.heappush(pq, min1 + min2)
except IndexError:
    pass

# do something with min1

# 使用异常处理来处理文件不存在的情况

def read_file(filename):
    try:
        with open(filename, 'r') as f:
            return f.read()
    except FileNotFoundError:
        return ""

# 使用异常处理来处理网络连接失败的情况

import requests

try:
    response = requests.get('https://example.com')
    print(response.text)
except requests.exceptions.ConnectionError:
    print("无法连接到服务器。")