如何处理列表推导中的异常?

问题:如何处理列表推导中的异常?

我在Python中有一些列表理解,其中每次迭代都可能引发异常。

例如,如果我有:

eggs = (1,3,0,3,2)

[1/egg for egg in eggs]

我将ZeroDivisionError在第3个元素中得到一个exceptions。

如何处理此异常并继续执行列表理解?

我能想到的唯一方法是使用辅助函数:

def spam(egg):
    try:
        return 1/egg
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

但这对我来说有点麻烦。

有没有更好的方法在Python中执行此操作?

注意: 这是我做的一个简单示例(请参阅上面的“ 例如 ”),因为我的实际示例需要一些上下文。我对避免除以零错误不感兴趣,但对处理列表理解中的异常不感兴趣。

I have some a list comprehension in Python in which each iteration can throw an exception.

For instance, if I have:

eggs = (1,3,0,3,2)

[1/egg for egg in eggs]

I’ll get a ZeroDivisionError exception in the 3rd element.

How can I handle this exception and continue execution of the list comprehension?

The only way I can think of is to use a helper function:

def spam(egg):
    try:
        return 1/egg
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

But this looks a bit cumbersome to me.

Is there a better way to do this in Python?

Note: This is a simple example (see “for instance” above) that I contrived because my real example requires some context. I’m not interested in avoiding divide by zero errors but in handling exceptions in a list comprehension.


回答 0

Python中没有内置表达式可让您忽略异常(或在异常的情况下返回替代值&c),因此从字面上来讲,“处理列表推导中的异常”是不可能的,因为列表推导是一个表达式包含其他表达式,仅此而已(即,没有语句,只有语句可以捕获/忽略/处理异常)。

函数调用是表达式,函数主体可以包含您想要的所有语句,因此,如您所注意到的,将易于发生异常的子表达式的评估委托给函数是一种可行的解决方法(其他可行时,检查可能引发异常的值,如其他答案中所建议)。

对“如何处理列表理解中的异常”这一问题的正确回答都表达了所有这些事实的一部分:1)从字面上,即从词法上讲,在理解本身中,你不能做到;2)实际上,在可行的情况下,您将作业委派给某个函数或检查易于出错的值。您一再声称这不是一个答案是没有根据的。

There is no built-in expression in Python that lets you ignore an exception (or return alternate values &c in case of exceptions), so it’s impossible, literally speaking, to “handle exceptions in a list comprehension” because a list comprehension is an expression containing other expression, nothing more (i.e., no statements, and only statements can catch/ignore/handle exceptions).

Function calls are expression, and the function bodies can include all the statements you want, so delegating the evaluation of the exception-prone sub-expression to a function, as you’ve noticed, is one feasible workaround (others, when feasible, are checks on values that might provoke exceptions, as also suggested in other answers).

The correct responses to the question “how to handle exceptions in a list comprehension” are all expressing part of all of this truth: 1) literally, i.e. lexically IN the comprehension itself, you can’t; 2) practically, you delegate the job to a function or check for error prone values when that’s feasible. Your repeated claim that this is not an answer is thus unfounded.


回答 1

我意识到这个问题已经很老了,但是您也可以创建一个通用函数来简化这种事情:

def catch(func, handle=lambda e : e, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return handle(e)

然后,在您的理解中:

eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]

当然,您可以随意设置默认的句柄函数(例如,您宁愿默认返回“ None”)。

希望这对您或该问题的将来的读者有帮助!

注意:在python 3中,我只将’handle’参数作为关键字,并将其放在参数列表的末尾。这将使实际传递的论点变得更加自然。

I realize this question is quite old, but you can also create a general function to make this kind of thing easier:

def catch(func, handle=lambda e : e, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return handle(e)

Then, in your comprehension:

eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]

You can of course make the default handle function whatever you want (say you’d rather return ‘None’ by default).

Hope this helps you or any future viewers of this question!

Note: in python 3, I would make the ‘handle’ argument keyword only, and put it at the end of the argument list. This would make actually passing arguments and such through catch much more natural.


回答 2

您可以使用

[1/egg for egg in eggs if egg != 0]

这只会跳过零元素。

You can use

[1/egg for egg in eggs if egg != 0]

this will simply skip elements that are zero.


回答 3

没有,没有更好的方法。在很多情况下,您可以像Peter一样使用回避

您的另一选择是不使用理解

eggs = (1,3,0,3,2)

result=[]
for egg in eggs:
    try:
        result.append(egg/0)
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

由您决定是否比较麻烦

No there’s not a better way. In a lot of cases you can use avoidance like Peter does

Your other option is to not use comprehensions

eggs = (1,3,0,3,2)

result=[]
for egg in eggs:
    try:
        result.append(egg/0)
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

Up to you to decide whether that is more cumbersome or not


回答 4

我认为,正如提出最初问题的人和布莱恩·海德(Bryan Head)所建议的那样,辅助功能很好,一点也不麻烦。单行执行所有工作的魔术代码总是不可能的,因此,如果要避免for循环,辅助函数是一个完美的解决方案。但是我将其修改为此:

# A modified version of the helper function by the Question starter 
def spam(egg):
    try:
        return 1/egg, None
    except ZeroDivisionError as err:
        # handle division by zero error        
        return None, err

输出将是this [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]。有了这个答案,您完全可以控制以任何您想要的方式继续。

I think a helper function, as suggested by the one who asks the initial question and Bryan Head as well, is good and not cumbersome at all. A single line of magic code which does all the work is just not always possible so a helper function is a perfect solution if one wants to avoid for loops. However I would modify it to this one:

# A modified version of the helper function by the Question starter 
def spam(egg):
    try:
        return 1/egg, None
    except ZeroDivisionError as err:
        # handle division by zero error        
        return None, err

The output will be this [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. With this answer you are in full control to continue in any way you want.

Alternative:

def spam2(egg):
    try:
        return 1/egg 
    except ZeroDivisionError:
        # handle division by zero error        
        return ZeroDivisionError

Yes, the error is returned, not raised.


回答 5

我没有任何答案提及此事。但是此示例将是一种防止在已知失败案例中引发异常的方法。

eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]


Output: [1, 0, None, 0, 0]

I didn’t see any answer mention this. But this example would be one way of preventing an exception from being raised for known failing cases.

eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]


Output: [1, 0, None, 0, 0]