在python中使用try vs if

时间:2009-12-02 20:58:38

标签: python

在测试变量有值时,是否有理由决定使用tryif构造中的哪一个?

例如,有一个函数返回列表或不返回值。我想在处理之前检查结果。以下哪一项更可取,为什么?

result = function();
if (result):
    for r in result:
        #process items

result = function();
try:
    for r in result:
        #process items
except TypeError:
    pass;

相关讨论:

Checking for member existence in Python

9 个答案:

答案 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编码   风格假定存在有效   键或属性和捕获   如果假设证明有例外   假。这种干净,快速的风格是   特点是存在许多   tryexcept语句。该   技术与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例外来控制的,你仍然应该更喜欢你的第一个代码片段到第二个。