在循环头中捕获异常但在循环体中不捕获

Catch exception in loop head but not in loop body

捕获发生在 *loop 头而不是整个循环或主体中的异常的最佳方法是什么。

举下面的例子

for value in complex_generator(): # throws exceptions I might want to catch
    ... # do work here - but don't catch any exception

我认为没有帮助的是将整个循环包装在 try 和 except 块中,如下所示:

try:
    for value in complex_generator(): # throws exceptions I might want to catch
        ... # exceptions raised here will also be caught :(
except Exception:
   ... # handle exception here

受 golang 的启发,可以封装 try-except-block 并且总是 return 两个元素:

def wrapper(iterable):
    try:
        for value in iterable:
            yield value, None
    except Exception as e:
        yield None, e


for value, err in wrapper(complex_generator()):
    if err != None:
        ... # handle error
    else:
        ... # do work but don't catch any exception here

然而,这感觉不像 pythonic,类型检查器也需要额外检查。有什么想法吗?

先获取发电机?

try:
  cg = complex_generator()
except Exception as e:
  cg = [] # could alternatively have a success boolean here, and wrap the `for` loop below in an `if`
for value in cg:
  ...

有两个级别的错误:生成器抛出的错误和您的工作程序代码抛出的错误。我会使用嵌套的 try:

try:
    for value in complex_generator():
        try:
            # do work here
        except ValueError:
            # catch ValueError and keep going
        except OtherError:
            # catch OtherError and keep going

        # any other error breaks the loop
except ExpectedGeneratorError:
    # handle generator exception here
except:
    # handle more errors

您可以将内部部分提取到辅助函数中以保持整洁。

我仍然认为@Tomalak 的回答是解决这个问题的最pythonic 方法。但是我想分享我最终使用的解决方案,因为 complex_generator 和循环体都可能引发相同的异常。

from typing import Iterable, Iterator, TypeVar


T = TypeVar("T")


def wrapper(iterable: Iterable[T]) -> Iterator[T | Exception]:
    try:
        for value in iterable:
            yield value
    except Exception as e:
        yield e


for value in wrapper(complex_generator()):
    if isinstance(value, Exception):
        handle_generator_error(value)
    else:
        try:
            process(value)
        except Exception as e:
            handle_loop_error(e)

注意:对于更复杂的场景,可以使用 match (structural pattern matching) 并将正文错误处理移动到 process 函数中。