问题:在python中使用try vs 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;
相关讨论:
回答 0
您通常会听到Python鼓励EAFP风格(“请求宽恕比许可容易”)而不是LBYL风格(“跨越前先看”)。对我来说,这是效率和可读性的问题。
在您的示例中(例如,该函数不是返回列表或空字符串,而是返回列表或None
),如果您希望99%的时间result
实际上包含可迭代的内容,则可以使用该try/except
方法。如果异常确实是exceptions,它将更快。如果result
是None
的时间超过50%,则使用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
块几乎是免费的。但是当Exception
实际发生时,成本要高得多。
道德:
try/except
用于流程控制完全可以(和“ pythonic”),- 但当
Exception
s实际上是exceptions时,这才有意义。
从Python文档中:
东亚自由贸易区
寻求宽恕比允许容易。这种通用的Python编码风格假设存在有效的键或属性,并且在假设被证明为假的情况下捕获异常。这种干净快捷的样式的特点是存在许多
try
和except
声明。该技术与 许多其他语言(例如C)通用的LBYL风格形成对比。
回答 1
您的函数不应返回混合类型(即列表或空字符串)。它应该返回一个值列表或一个空列表。然后,您无需进行任何测试,即您的代码折叠为:
for r in function():
# process items
回答 2
如果我提供的代码乍一看不明显,请忽略我的解决方案,而您必须在代码示例之后阅读说明。
我可以假定“没有返回值”意味着返回值为None吗?如果是,或者如果“ no value”是boolean值,则可以执行以下操作,因为您的代码实际上将“ no value”视为“请勿迭代”:
for r in function() or ():
# process items
如果function()
返回的结果不是True,则对空元组进行迭代,即不运行任何迭代。这本质上是LBYL。
回答 3
您的第二个示例已损坏-该代码将永远不会引发TypeError异常,因为您可以遍历字符串和列表。遍历空字符串或列表也是有效的-它将执行循环主体0次。
回答 4
以下哪一项更可取,为什么?
在这种情况下,“跨越前先看”更可取。使用异常方法,TypeError可能会在循环主体中的任何位置发生,并且会被捕获并丢弃,这不是您想要的,而且会使调试变得棘手。
(尽管我同意布兰登·考夫曼的观点:返回“无项目”而不是空列表的做法是无效的。这是Java编码器的不愉快习惯,不应在Python或Java中看到。)
回答 5
通常,我得到的印象是exceptions应保留用于特殊情况。如果result
预期永远不会为空(例如,如果磁盘崩溃等可能为空),则第二种方法很有意义。另一方面,如果result
在正常情况下清空是完全合理的,请使用if
语句进行更有意义。
我想到了(更常见的)情况:
# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
file_counts[filename]=0
file_counts[filename]+=1
而不是等效的:
...
try:
file_counts[filename]+=1
except KeyError:
file_counts[filename]=1
回答 6
bobince明智地指出,包装第二种情况也可以在循环中捕获TypeErrors,这不是您想要的。如果您确实想尝试一下,可以在循环之前测试它是否可迭代
result = function();
try:
it = iter(result)
except TypeError:
pass
else:
for r in it:
#process items
如您所见,这非常难看。我不建议这样做,但出于完整性考虑。
回答 7
就性能而言,对于通常不引发异常的代码使用try块比每次使用if语句要快。因此,决定取决于发生特大案件的可能性。
回答 8
作为一般经验法则,切勿使用try / catch或任何异常处理工具来控制流程。即使幕后的迭代是通过引发StopIteration
异常来控制的,您仍然应该优先选择第一个代码段而不是第二个代码段。