现在,我的代码中有些内容是这样的:
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)
是否会失败)。
我感兴趣的是拥有这个result
:f
所有有效结果的列表。
我想知道是否有一种方法可以使第二部分像列表理解一样。当然,我不能简单地做到这一点:
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。
答案 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)))