好吧,我过去曾多次看到过这种说法,但最近是在我的
try..except
问题。所以,我很好奇为什么会出现这种情况,在 Python 中,因为生成器使用异常来指示数据的结尾。
如果这对每个使用 Python 的人来说都如此糟糕,为什么该语言将其包含在被认为是基本控制结构中?对于那些想要阅读相关 PEP 的人,请参阅PEP 255:简单生成器。
因为结束生成器不是一个常见事件(我知道它总会发生,但只发生一次)。抛出异常被认为是昂贵的。如果一个事件在 99% 的情况下成功,1% 的情况失败,那么使用 try/ except 比检查是否可以访问该数据要快得多(请求原谅比请求许可更容易)。
也存在对它的偏见,因为这样使用的 try/ except 块可能非常难以理解。流程控制可能很难遵循,而 if/else 则更简单。 try/ except 意味着你必须跟踪它调用的函数内部 try 和 内的语句的流程控制(因为它们可能抛出异常并且它可能向上传播。 if/else 只能在该点分支当评估语句时。
有时使用 try/ except 是正确的,有时使用 if/else 更有意义。它们中的每一个都存在相关的性能成本。考虑:
a = <some dictionary>
if key in a:
print a[key]
与
a = <some dictionary>
try:
print a[key]
except KeyError:
pass
如果 a 内部不存在 key,第一个会更快,如果确实存在,第一个会稍微慢一点(几乎察觉不到)。如果密钥确实存在,第二个会更快,但如果不存在,第二个会慢得多。如果密钥几乎总是存在,则选择第二个。否则,第一个效果更好。
编辑:只需添加一点关于 Python try/ except 的东西,这对解决可读性问题有很大帮助。
考虑从文件中读取。
f = None
try:
f = open(filename, 'r')
... do stuff to the file ...
except (IOError, OSError):
# I can never remember which one of these Python throws...
... handle exception ...
finally:
if f:
f.close()
现在
do stuff to the file
中的任何内容都可以抛出异常,我们将捕获它。通常,出于这个原因,您会尝试在 try 中保留尽可能少的代码。 Python 有一个可选的 else
子句用于 try,只有当 try 运行完成且没有遇到异常时才会运行。
f = None
try:
f = open(filename, 'r')
except (IOError, OSError):
pass
else:
... do stuff to the file ...
finally:
if f:
f.close()
在这种情况下,您不会遇到任何可读性问题,因为 try 中只有一条语句;这是一个Python标准库函数调用,你只捕获特定的异常。
因为异常是在堆栈中向上引发的,所以它们适用于“某些”情况,您希望使用其他代码的代码能够捕获异常。 例如,考虑一下您想要创建一个更“手动”地使用另一个迭代器的一部分的迭代器的情况,拥有一个可以从堆栈高层捕获并插入您自己的逻辑的异常是很有价值的。
try
块进行流量控制可能会生成如下代码:
try:
# stuff
try:
if userCondition:
raise NeedToDoSomethingElseException
try:
# stuff
except NeedToDoSomethingElseException:
# other stuff
except NeedToDoSomethingElseException:
# other stuff
except NeedToDoSomethingElseException:
# other stuff
抛开性能问题不谈,这并不是很优雅。 因此,有时使用
try
是完全合适的,但并非适用于所有情况。