在测试变量有值时,是否有理由决定使用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 :(得分:192)
你经常听到Python鼓励EAFP样式(“比同意更容易请求宽恕”)超过LBYL样式(“先看看你跳跃”)。对我来说,这是效率和可读性的问题。
在你的例子中(如果你预计99%的时间None
实际上会发送,而不是返回一个列表或一个空字符串,该函数将返回一个列表或result
)包含可迭代的东西,我使用try/except
方法。如果异常真的很特殊,它会更快。如果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
实际上是异常时,它最有意义。 来自Python文档:
<强> EAFP 强>
比请求宽恕更容易 允许。这是常见的Python编码 风格假定存在有效 键或属性和捕获 如果假设证明有例外 假。这种干净,快速的风格是 特点是存在许多
try
和except
语句。该 技术与LBYL形成鲜明对比 许多其他语言共有的风格 例如C.
答案 1 :(得分:13)
您的函数不应返回混合类型(即列表或空字符串)。它应该返回一个值列表或只返回一个空列表。然后你不需要测试任何东西,即你的代码崩溃到:
for r in function():
# process items
答案 2 :(得分:10)
如果我提供的代码乍一看并且您必须在代码示例后阅读说明,请忽略我的解决方案。
我可以假设“没有返回值”表示返回值为None吗?如果是,或者如果“无值”是布尔方式的假,则可以执行以下操作,因为您的代码基本上将“无值”视为“不迭代”:
for r in function() or ():
# process items
如果function()
返回的内容不是True,则迭代空元组,即不运行任何迭代。这基本上是LBYL。
答案 3 :(得分:5)
您的第二个示例已损坏 - 代码永远不会抛出TypeError异常,因为您可以遍历字符串和列表。迭代空字符串或列表也是有效的 - 它将执行循环体零次。
答案 4 :(得分:4)
一般来说,我得到的印象是应该为特殊情况保留例外情况。如果期望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
答案 5 :(得分:4)
以下哪项更为可取?为什么?
在这种情况下,优先考虑跳跃是最好的。使用异常方法时,TypeError可能会出现在循环体中的任何位置,并且会被捕获并丢弃,这不是您想要的,并且会使调试变得棘手。
(我同意Brandon Corfman的观点:对于'no items'而不是空列表返回None是坏的。这是Java编码器的一种令人不快的习惯,不应该在Python中看到。或者Java。)
答案 6 :(得分:3)
bobince明智地指出包装第二种情况也可以捕获循环中的TypeErrors,这不是你想要的。如果你真的想尝试使用,你可以测试它是否可以在循环之前迭代
result = function();
try:
it = iter(result)
except TypeError:
pass
else:
for r in it:
#process items
正如你所看到的,它相当难看。我不建议,但应该提到完整性。
答案 7 :(得分:1)
就性能而言,通常使用try块代码 不会引发异常比每次使用if语句更快。因此,决定取决于例外情况的概率。
答案 8 :(得分:-5)
作为一般经验法则,你应该永远使用try / catch或任何异常处理来控制流程。即使幕后迭代是通过引发StopIteration
例外来控制的,你仍然应该更喜欢你的第一个代码片段到第二个。