我目前正在尝试使用类似生成器的行为来允许快速上下文切换和并发,而不会产生多进程的开销和复杂性,这工作得很好但是我正在尝试找到一种方法让这些迭代器在引发后恢复一个异常(exception)。
据我所知,生成器在引发异常后技术上是不可能恢复的,如下所示:
def test():
yield 1
raise KeyboardInterrupt
yield 2
a = test()
for x in range(2):
try:
print('Trying next...')
print(next(a))
except:
pass
结果:
Trying next...
1
Trying next...
第二个产量从未达到。这是预期的行为。
我正在寻找的是允许这样的生成器运行,在生成器外部处理异常,然后让生成器保留旧上下文(可能由异常处理代码)然后继续执行。
目前我有以下设置:
class base:
def __iter__(self):
self._run = self.main()
return self
def __next__(self,_run=None):
try:
return next(self._run)
except tuple(self.exceptions.keys()) as e: # if exception handler exists, use it
exception = e.__class__
self._run = self._end() # end thread if the handler also fails
handler = self.__next__(self.exceptions[exception])
if handler: # if handler returns something use it, else it should replace _run manually, otherwise thread will end
self._run = handler
except (StopIteration,exceptions.u_end):
#Execution done
raise
except:
#unhandled excpetion, should log
raise
def _end(self):
raise exceptions.u_end
yield
def main(self):
"""Replace me with something useful"""
self.exceptions = {exceptions.u_reset:self._reset} #set handler
for x in range(2):
print("Main:",x)
yield x
raise exceptions.u_reset
print('This should NEVER be run!!!!')
def _reset(self):
self.__iter__()
这在处理可能随时失败的网络代码时非常有效,并且添加更多异常处理程序允许运行不同的代码以在重试主代码之前解决某些问题。
理想情况下,我希望能够使用这些异常来解决异常并继续运行 main 而不必从头重新启动 main(即,如果出现网络问题,网络错误处理程序会重新连接,这会很好从我们离开的地方继续,而不是在这种情况下从头开始重新启动 main)
我知道一般的答案是“如果不重写生成器就无法完成”,但我正在寻找一个 pythonic hack 来让它工作。
一种可能的方法是用我自己的状态机替换 main,但是这会失去使用迭代器的好处。此问题假定这不是有效答案。
我一直在研究使用 exec 并实际从 main 读取代码行并一次运行它们的可能性,这将允许我在每一行代码上放置一个 try/catch 并允许我重新- 在调用异常处理程序后尝试该行,但这会导致巨大的性能损失,并且感觉像是一件邪恶的事情。
还有哪些其他选择?是否有允许此类功能的任何调试模块?
编辑: 澄清一下,我正在寻找一种不涉及尝试/捕获 main 中的所有内容的方法。如果捕获到异常,则有可能在继续之前产生有关异常的信息,但是这不会分配失败的行重新运行并且会导致太多的尝试/异常。这里的想法是允许代码自己进行最少的错误处理,以便使用外部错误处理程序。
编辑:Joran 提出了以下几乎可行的技术:
def safe_test(t):
while True:
try:
yield next(t)
except StopIteration:
print('stop iteration')
break
except BaseException as e:
print('exception yielded')
yield e
def test():
yield 1
raise KeyboardInterrupt
yield 2
for x in safe_test(test()):
print('trying next')
print(x)
这会起作用,但是捕获异常实际上会停止生成器,任何其他在此生成器上调用 next 的尝试都会导致 StopIteration,如下所示:
trying next
1
exception yielded
trying next
stop iteration
如果完全正常工作,预期的结果是:
trying next
1
exception yielded
trying next
2
trying next
stop iteration
最佳答案
嗯嗯
def some_generator():
for i in range(100):
if i % 25 == 0:
yield Exception("whatever")
yield i
for val in some_generator():
if isinstance(val,Exception):
print ("some Error happened....",val)
else:
print(val)
但实际上听起来您正试图滥用异常机制并以不打算使用的方式使用它...
根据你的评论可能是这样的
def some_generator(some_function):
for i in range(100):
try:
yield some_function(i)
except Exception as e:
yield e
def a_fn(x):
return x/(x%25)
for val in some_generator(a_fn):
if isinstance(val,Exception):
print ("some Error happened....",val)
else:
print(val)
关于python - 如何重新进入上次引发异常的迭代器/生成器对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21005548/