在一些地方(这里和这里)已经说过,Python强调"请求宽恕而不是许可"(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
Run Code Online (Sandbox Code Playgroud)
异常仅在len(a_list)循环的迭代中引发一次,但它并不是特别的,因为我们知道它最终会发生.这个设置使我们a_list无需检查是否空了很多次,但(可能)它比使用显式条件更不易读.
对这种非特殊程序逻辑使用异常的共识是什么?
Ale*_*lli 30
只应在真正例外情况下调用异常
不是在Python中:例如,每个 for循环(除非过早地breaks或returns StopIteration)都会被抛出并捕获的异常()终止.因此,每个循环发生一次的异常对Python来说并不陌生 - 它经常出现在那里!
有问题的原则在其他语言中可能是至关重要的,但这绝对没有理由将该原则应用于Python,因为它与语言的精神背道而驰.
在这种情况下,我喜欢Jon的重写(应该通过删除else分支进一步简化),因为它使代码更紧凑 - 一个实用的原因,绝对不是具有外来原则的Python风格的"调和".
在大多数低级语言(如C++)中抛出异常代价很高.这影响了许多关于异常的"常识",并不适用于在VM中运行的语言,如Python.Python中使用异常而不是条件的成本并不高.
(这是"常识"成为习惯问题的情况.人们从一种环境 - 低级语言 - 中获得经验,然后将其应用于新领域而不评估是否有意义. )
一般而言,例外仍然是例外.这并不意味着它们不经常发生; 这意味着它们是例外.它们往往会破坏普通代码流,而且大多数时候你不想逐个处理 - 这是异常处理程序的重点.这部分在Python中与在C++中相同,所有其他语言都有例外.
但是,这往往会定义何时抛出异常.你在谈论应该抓住异常的时候.非常简单,不要担心:异常并不昂贵,所以不要竭尽全力防止它们被抛出.围绕这个设计了很多Python代码.
我不同意Jon的建议,试图提前测试并避免异常.如果它导致更清晰的代码,那就没问题,就像在他的例子中那样.但是,在许多情况下,它只会使事情变得复杂 - 它可以有效地导致重复检查和引入错误.例如,
import os, errno, stat
def read_file(fn):
"""
Read a file and return its contents. If the file doesn't exist or
can't be read, return "".
"""
try:
return open(fn).read()
except IOError, e:
return ""
def read_file_2(fn):
"""
Read a file and return its contents. If the file doesn't exist or
can't be read, return "".
"""
if not os.access(fn, os.R_OK):
return ""
st = os.stat(fn)
if stat.S_ISDIR(st.st_mode):
return ""
return open(fn).read()
print read_file("x")
Run Code Online (Sandbox Code Playgroud)
当然,我们可以测试并避免失败 - 但我们会把事情弄得很复杂.我们试图猜测文件访问可能失败的所有方式(这并没有捕获所有这些),我们可能已经引入了竞争条件,而且我们正在做更多的I/O工作.这一切都是为我们完成的 - 只是捕获异常.
查看文档,我认为您可以安全地重写函数,如下所示:
import heapq
...
pq = heapq.heapify(a_list)
while pq:
min1 = heapq.heappop(pq)
if pq:
min2 = heapq.heappop(pq)
heapq.heappush(pq, min1 + min2)
# do something with min1
Run Code Online (Sandbox Code Playgroud)
..从而避免尝试 - 除外.
到达列表的末尾,这是你知道会在这里发生的事情并不是特例 - 它是天真的!因此,更好的做法是提前处理它.如果你在另一个线程中有其他东西在同一堆中消耗,那么使用try-except会有更多的意义(即处理特殊/不可预测的情况).
更一般地说,无论我在哪里测试,我都会避免尝试 - 并提前避免失败.这迫使你说"我知道这种糟糕的情况可能会发生,所以这就是我如何处理它".在我看来,你会倾向于编写更可读的代码.
[编辑]根据Alex的建议更新了示例
| 归档时间: |
|
| 查看次数: |
2302 次 |
| 最近记录: |