152

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

例如,如果我有:

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

[1/egg for egg in eggs]

我会ZeroDivisionError在第三个元素中得到一个例外。

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

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

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

但这对我来说有点麻烦。

有没有更好的方法在 Python 中做到这一点?

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

4

8 回答 8

151

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

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]

您当然可以随意设置默认句柄函数(假设您宁愿默认返回“无”)。

希望这对您或该问题的任何未来观众有所帮助!

注意:在 python 3 中,我将只使用“handle”参数关键字,并将其放在参数列表的末尾。这将使实际通过 catch 传递参数等更加自然。

更新(9 年后...):对于 Python 3,我只是指 switch*argshandle,因此您可以在不指定句柄的情况下为函数指定参数。一个小便利:

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

这在理解中使用定义的函数时很有帮助:

from math import log    
eggs = [1,3,0,3,2]
[catch(log, egg) for egg in eggs]
[0.0, 1.0986122886681098, ValueError('math domain error'), 1.0986122886681098, 0.6931471805599453]

在 Python 2 版本下,我们必须handleegg.

于 2012-01-18T18:48:01.453 回答
125

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

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

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

于 2009-10-06T22:10:27.077 回答
19

您可以使用

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

这将简单地跳过零元素。

于 2009-10-06T21:38:18.953 回答
13

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

您的另一个选择是不使用推导

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

由您决定是否更麻烦

于 2009-10-06T21:50:01.713 回答
7

我认为,正如最初提出问题的人和 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)]。有了这个答案,您可以完全控制以您想要的任何方式继续。

替代方案

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

是的,错误被返回,而不是引发。

于 2016-11-08T10:23:26.670 回答
5

我没有看到任何答案提到这一点。但是此示例将是防止为已知的失败案例引发异常的一种方法。

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


Output: [1, 0, None, 0, 0]
于 2019-10-25T23:57:34.213 回答
0

您可以使用生成器:

def invert(xs):
    for x in xs:
        try:
            yield x
        except:
            yield None

list(invert(eggs))
于 2021-05-26T11:59:22.547 回答
-2

我是编程新手,所以只是在想这种方式是否可行......

>>> eggs = (1,3,0,3,2)
>>> try:
>>>     [1/egg for egg in eggs]
>>> except ZeroDivisionError as e:
>>>     print(e)


>>> division by zero
于 2021-05-14T20:37:59.073 回答