在python中使用try vs if

问题:在python中使用try vs if

测试变量具有值时,是否有理由确定要使用哪个tryif构造?

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

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

要么

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

相关讨论:

在Python中检查成员是否存在

Is there a rationale to decide which one of try or if constructs to use, when testing variable to have a value?

For example, there is a function that returns either a list or doesn’t return a value. I want to check result before processing it. Which of the following would be more preferable and why?

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

or

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

Related discussion:

Checking for member existence in Python


回答 0

您通常会听到Python鼓励EAFP风格(“请求宽恕比许可容易”)而不是LBYL风格(“跨越前先看”)。对我来说,这是效率和可读性的问题。

在您的示例中(例如,该函数不是返回列表或空字符串,而是返回列表或None),如果您希望99%的时间result实际上包含可迭代的内容,则可以使用该try/except方法。如果异常确实是exceptions,它将更快。如果resultNone的时间超过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”),
  • 但当Exceptions实际上是exceptions时,这才有意义。

从Python文档中:

东亚自由贸易区

寻求宽恕比允许容易。这种通用的Python编码风格假设存在有效的键或属性,并且在假设被证明为假的情况下捕获异常。这种干净快捷的样式的特点是存在许多 tryexcept声明。该技术与 许多其他语言(例如C)通用的LBYL风格形成对比。

You often hear that Python encourages EAFP style (“it’s easier to ask for forgiveness than permission”) over LBYL style (“look before you leap”). To me, it’s a matter of efficiency and readability.

In your example (say that instead of returning a list or an empty string, the function were to return a list or None), if you expect that 99 % of the time result will actually contain something iterable, I’d use the try/except approach. It will be faster if exceptions really are exceptional. If result is None more than 50 % of the time, then using if is probably better.

To support this with a few measurements:

>>> 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

So, whereas an if statement always costs you, it’s nearly free to set up a try/except block. But when an Exception actually occurs, the cost is much higher.

Moral:

  • It’s perfectly OK (and “pythonic”) to use try/except for flow control,
  • but it makes sense most when Exceptions are actually exceptional.

From the Python docs:

EAFP

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.


回答 1

您的函数不应返回混合类型(即列表或空字符串)。它应该返回一个值列表或一个空列表。然后,您无需进行任何测试,即您的代码折叠为:

for r in function():
    # process items

Your function should not return mixed types (i.e. list or empty string). It should return a list of values or just an empty list. Then you wouldn’t need to test for anything, i.e. your code collapses to:

for r in function():
    # process items

回答 2

如果我提供的代码乍一看不明显,请忽略我的解决方案,而您必须在代码示例之后阅读说明。

我可以假定“没有返回值”意味着返回值为None吗?如果是,或者如果“ no value”是boolean值,则可以执行以下操作,因为您的代码实际上将“ no value”视为“请勿迭代”:

for r in function() or ():
    # process items

如果function()返回的结果不是True,则对空元组进行迭代,即不运行任何迭代。这本质上是LBYL。

Please ignore my solution if the code I provide is not obvious at first glance and you have to read the explanation after the code sample.

Can I assume that the “no value returned” means the return value is None? If yes, or if the “no value” is False boolean-wise, you can do the following, since your code essentially treats “no value” as “do not iterate”:

for r in function() or ():
    # process items

If function() returns something that’s not True, you iterate over the empty tuple, i.e. you don’t run any iterations. This is essentially LBYL.


回答 3

您的第二个示例已损坏-该代码将永远不会引发TypeError异常,因为您可以遍历字符串和列表。遍历空字符串或列表也是有效的-它将执行循环主体0次。

Your second example is broken – the code will never throw a TypeError exception since you can iterate through both strings and lists. Iterating through an empty string or list is also valid – it will execute the body of the loop zero times.


回答 4

以下哪一项更可取,为什么?

在这种情况下,“跨越前先看”更可取。使用异常方法,TypeError可能会在循环主体中的任何位置发生,并且会被捕获并丢弃,这不是您想要的,而且会使调试变得棘手。

(尽管我同意布兰登·考夫曼的观点:返回“无项目”而不是空列表的做法是无效的。这是Java编码器的不愉快习惯,不应在Python或Java中看到。)

Which of the following would be more preferable and why?

Look Before You Leap is preferable in this case. With the exception approach, a TypeError could occur anywhere in your loop body and it’d get caught and thrown away, which is not what you want and will make debugging tricky.

(I agree with Brandon Corfman though: returning None for ‘no items’ instead of an empty list is broken. It’s an unpleasant habit of Java coders that should not be seen in Python. Or 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

Generally, the impression I’ve gotten is that exceptions should be reserved for exceptional circumstances. If the result is expected never to be empty (but might be, if, for instance, a disk crashed, etc), the second approach makes sense. If, on the other hand, an empty result is perfectly reasonable under normal conditions, testing for it with an if statement makes more sense.

I had in mind the (more common) scenario:

# 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

instead of the equivalent:

...
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

如您所见,这非常难看。我不建议这样做,但出于完整性考虑。

bobince wisely points out that wrapping the second case can also catch TypeErrors in the loop, which is not what you want. If you do really want to use a try though, you can test if it’s iterable before the loop

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

As you can see, it’s rather ugly. I don’t suggest it, but it should be mentioned for completeness.


回答 7

就性能而言,对于通常不引发异常的代码使用try块比每次使用if语句要快。因此,决定取决于发生特大案件的可能性。

As far as the performance is concerned, using try block for code that normally doesn’t raise exceptions is faster than using if statement everytime. So, the decision depends on the probability of excetional cases.


回答 8

作为一般经验法则,切勿使用try / catch或任何异常处理工具来控制流程。即使幕后的迭代是通过引发StopIteration异常来控制的,您仍然应该优先选择第一个代码段而不是第二个代码段。

As a general rule of thumb, you should never use try/catch or any exception handling stuff to control flow. Even though behind the scenes iteration is controlled via the raising of StopIteration exceptions, you still should prefer your first code snippet to the second.