可以多次(或类似)从上下文管理器中产生

问题描述 投票:0回答:1

我想在处理列表时报告多个错误: 例如:

with MultipleExceptions.testItems(items) as value:
   ... process value
   if value==3: raise Exception("3 err")
   if value==5: raise Exception("5 err")

可能应该收集并报告块中引发异常的项目的每个值的异常(不仅仅是第一个)。

天真的实现:

class MultipleExceptions(Exception)
   @staticmethod
   @contextmanager
   def testAllItems(items):
       errs = []
       for i, value in enumerate(items):
          try:
             yield value
          except Exception e:
             errs.append(i,e,value)

       if errs: ... compose and raise mega exception message

...失败,因为上下文管理器只能产生一次。尚不清楚为什么这是真的。有没有办法可以多次yield?

作为发电机:

for value in MultipleExceptions.testAllItems(items):
   ... process value
   if value==3: raise Exception("3 err")
   if value==5: raise Exception("5 err")
class MultipleExceptions(Exception)
   @staticmethod
   def testAllItems(items):
       errs = []
       for i, value in enumerate(items):
          try:
             yield value
          except Exception e:
             errs.append(i,e,value)

       if errs: ... compose and raise mega exception message

...失败,因为 testAllItems 中从未捕获异常。

显然我可以将我的块包装到一个函数中并将其提供给实用程序(没有yield),但是使用生成器或上下文管理器yield到一个块似乎更干净。可以吗?

python yield contextmanager
1个回答
0
投票

不幸的是,无法从 Python 中的 raise 语句进行

resume
。一旦将调用堆栈展开到特定点,就无法返回。有些语言确实允许跳回错误的堆栈帧,例如 Raku 的
resume
Common Lisp 的
continue
,但此功能在 Python 中不可用。

一般来说,我们在Python中倾向于做的就是积累一个错误列表并一次性抛出它们。它不像您建议的上下文管理器那样漂亮或自动,但它有效。

errors_list = []
if value == 3:
    errors_list.append(Exception("3 err"))
if value == 5:
    errors_list.append(Exception("5 err"))

# At the end ...
if errors_list:
    raise MyCustomError(errors_list)

事实上,Python 3.11 及更高版本带有 异常组 就是这样做的。您仍然需要自己累积错误,但是

ExceptionGroup
提供了一个很好的
except*
语法来处理不同的错误类型。

errors_list = []
if value == 3:
    errors_list.append(Exception("3 err"))
if value == 5:
    errors_list.append(Exception("5 err"))

# At the end ...
if errors_list:
    raise ExceptionGroup("Message summarizing the exceptions", errors_list)

然后,处理它们,

try:
    call_possibly_failing_function()
except* SomeExceptionType as e:
    # Handle all the SomeExceptionType's in the group ...
except* SomeOtherExceptionType as e:
    # Handle all the SomeOtherExceptionType's in the group ...
except* Exception as e:
    # Handle all uncaught exceptions ...

请注意,在这些

except*
块中(与普通
except
块相反),变量
e
的类型为
ExceptionGroup
。您可以使用 迭代该组中的 中的异常

for exc in e.exceptions: ...
    
© www.soinside.com 2019 - 2024. All rights reserved.