有没有更优雅的方法来过滤函数的失败结果?

时间:2019-05-08 21:24:41

标签: python python-3.x

现在,我的代码中有些内容是这样的:

def f(x):
    if x == 5:
        raise ValueError
    else:
        return 2 * x

interesting_values = range(10)
result = []
for i in interesting_values:
    try:
        result.append(f(i))
    except ValueError:
        pass

f实际上是一个更复杂的函数,它以不可预测的方式针对特定值失败(在尝试之前,我不知道f(x)是否会失败)。

我感兴趣的是拥有这个resultf所有有效结果的列表。

我想知道是否有一种方法可以使第二部分像列表理解一样。当然,我不能简单地做到这一点:

def f(x):
    if x == 5:
        raise ValueError
    else:
        return 2 * x

interesting_values = range(10)
result = [f(i) for i in interesting_values]

因为对f(5)的调用将使所有操作失败,但是也许有一种方法可以将try-except结构集成到列表推导中。是这样吗?

编辑:我可以控制f。

3 个答案:

答案 0 :(得分:6)

似乎您可以控制f并可以修改其处理错误的方式。

如果是这种情况,并且None不是该函数的有效输出,我希望它在发生错误时返回None而不是抛出:

def f(x):
    if x == 5: return None
    else: return 2*x

然后对其进行过滤:

results = (f(x) for x in interesting_values) # A generator expression; almost a list comptehension

valid_results = filter(lambda x: x is not None, results)

这是通常称为“可选模式”的简化版本。传回错误的特殊警戒值(在这种情况下为None),否则,返回有效值。通常,Optional类型是一种特殊类型,而sentinal值是该类型的子类(或类似的东西),但这在这里不是必需的。

答案 1 :(得分:4)

我在这里假设您无法控制f的来源。如果这样做的话,第一个建议就是简单地重写f而不引发异常,因为很明显,您期望该执行路径会发生,根据定义,它不会成为 exception 。但是,如果您无法控制它,请继续阅读。

如果您的某个函数可能会失败,并且希望忽略其“失败”,则始终可以包装该函数

def safe_f(x):
  try:
    return f(x)
  except ValueError:
    return None

result = filter(lambda x: x is not None, map(safe_f, values))

当然,如果在某些情况下f可以返回None,则必须使用不同的哨兵值。如果所有其他方法都失败了,您总是可以定义自己的_sentinel = object()并与之进行比较。

答案 2 :(得分:4)

您可以在功能之上添加另一层。装饰器,如果可以的话,可以将异常转换为更有用的东西。实际上,这是一个返回装饰器的函数,因此增加了两层:

from functools import wraps

def transform(sentinel=None, err_type=ValueError):
    def decorator(f):
        @wraps(f)
        def func(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except err_type:
                return sentinel
        return func
    return decorator

@transform()
def f(...): ...

interesting = range(10)
result = [y for y in (f(x) for x in interesting) if y is not None]

此解决方案是为从其他地方获得f而设计的。您可以调整transform来为给定的一组异常返回一个修饰符,并返回None以外的哨兵值(如果这是有效的返回值)。例如,如果您导入f,并且除TypeError之外还可以提高ValueError,则它看起来像这样:

from mystuff import f, interesting

sentinel = object()
f = transform(sentinel, (ValueError, TypeError))(f)
result = [y for y in (f(x) for x in interesting) if y is not sentinel]

您还可以使用理解元素的功能版本:

result = list(filter(sentinel.__ne__, map(f, interesting)))