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

187 投票
7 回答
109343 浏览
提问于 2025-04-15 14:52

我在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中有没有更好的方法呢?

注意: 这是一个简单的例子(见上面的“举个例子”),我编造这个例子是因为我真实的例子需要一些背景。我并不是想避免除以零的错误,而是想在列表推导式中处理异常。

7 个回答

23

你可以使用

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

这样做会简单地跳过值为零的元素。

180

我知道这个问题已经很久了,但你可以创建一个通用的函数来让这类事情变得更简单:

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'这个参数设置为关键字参数,并把它放在参数列表的最后面。这样在通过catch传递参数时会显得更自然。

更新(9年后...):对于Python 3,我的意思是交换*argshandle的位置,这样你可以在不指定handle的情况下给函数传递参数。这是一个小小的便利:

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版本中,我们必须在egg之前传入handle

141

在Python中,没有内置的表达式可以让你忽略异常(或者在出现异常时返回其他值),所以从字面上说,在列表推导式中“处理异常”是不可能的,因为列表推导式只是包含其他表达式的一个表达式,仅此而已(也就是说,没有语句,而只有语句才能捕获/忽略/处理异常)。

函数调用是表达式,而函数的主体可以包含你想要的所有语句。所以,正如你所注意到的,将可能出错的子表达式的评估委托给一个函数,是一个可行的解决办法(其他的解决办法包括在可能的情况下检查可能引发异常的值,这在其他答案中也有提到)。

对于“如何在列表推导式中处理异常”这个问题,正确的回答都表达了这个真相的一部分:1)从字面上说,也就是在推导式本身中,你是做不到的;2)实际上,你可以把这个工作交给一个函数,或者在可能的情况下检查可能出错的值。因此,你反复声称这不是一个答案的说法是没有根据的。

撰写回答