在Python中使用'try'与'if
在决定使用 try
还是 if
来检查一个变量是否有值时,有什么考虑的理由吗?
举个例子,有一个函数可能返回一个列表,也可能什么都不返回。我想在处理这个结果之前先检查一下。下面这两种方式,哪种更好呢?为什么?
result = function();
if (result):
for r in result:
#process items
或者
result = function();
try:
for r in result:
# Process items
except TypeError:
pass;
相关讨论:
9 个回答
如果我提供的代码一开始看起来不太明显,请忽略我的解决方案,先看代码示例后再读解释。
我可以假设“没有返回值”意味着返回值是None吗?如果可以,或者如果“没有值”在布尔逻辑上是False,那么你可以这样做,因为你的代码实际上把“没有值”当作“不要进行迭代”来处理:
for r in function() or ():
# process items
如果function()
返回的不是True,你就会遍历一个空的元组,也就是说你不会进行任何迭代。这基本上就是一种“先检查后行动”的方式。
你的函数不应该返回混合类型的结果(比如同时返回列表和空字符串)。它应该只返回一个值的列表,或者仅仅是一个空列表。这样你就不需要进行任何测试了,也就是说,你的代码可以简化成:
for r in function():
# process items
你经常会听到Python提倡一种叫做EAFP的风格(“请求原谅比请求许可更容易”),而不是LBYL风格(“跳之前先看看”)。在我看来,这主要是关于效率和可读性的问题。
在你的例子中(假设这个函数返回的是一个列表或者None
,而不是返回一个列表或空字符串),如果你预计99%的情况下result
会包含一些可迭代的内容,我会选择使用try/except
的方式。如果异常真的很少发生,这样会更快。如果result
超过50%的时间是None
,那么使用if
可能会更好。
为了支持这个观点,我做了一些测量:
>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175
所以,if
语句总是会带来一些开销,而设置一个try/except
块几乎是免费的。但是当真的发生异常时,成本就会高很多。
总结:
- 使用
try/except
来控制流程是完全可以的(而且是“Python风格”的), - 但当异常确实是少数情况时,这种方式最有意义。
来自Python文档的说明:
EAFP
请求原谅比请求许可更容易。这种常见的Python编码风格假设有效的键或属性是存在的,并在假设错误时捕获异常。这种干净且快速的风格以大量的
try
和except
语句为特征。这种技术与许多其他语言(如C语言)常用的LBYL风格形成对比。