私はこの問題を数回解決する必要があり、他の人が何をしたかを調べた後、この質問に出くわしました。
レイズの代わりに投げる
1つのオプション(少しリファクタリングが必要になりthrow
ます)は、ジェネレーターの例外(別のエラー処理ジェネレーター)ではなく、例外raise
です。これがどのように見えるかです:
def read(handler):
# the handler argument fixes errors/problems separately
while something():
try:
yield something_else()
except Exception as e:
handler.throw(e)
handler.close()
def err_handler():
# a generator for processing errors
while True:
try:
yield
except Exception1:
handle_exc1()
except Exception2:
handle_exc2()
except Exception3:
handle_exc3()
except Exception:
raise
def process():
handler = err_handler()
handler.send(None) # initialize error handler
for item in read(handler):
do stuff
これが常に最良の解決策になるとは限りませんが、それは確かにオプションです。
一般化されたソリューション
デコレータを使用すると、すべてを少しだけ良くすることができます。
class MyError(Exception):
pass
def handled(handler):
"""
A decorator that applies error handling to a generator.
The handler argument received errors to be handled.
Example usage:
@handled(err_handler())
def gen_function():
yield the_things()
"""
def handled_inner(gen_f):
def wrapper(*args, **kwargs):
g = gen_f(*args, **kwargs)
while True:
try:
g_next = next(g)
except StopIteration:
break
if isinstance(g_next, Exception):
handler.throw(g_next)
else:
yield g_next
return wrapper
handler.send(None) # initialize handler
return handled_inner
def my_err_handler():
while True:
try:
yield
except MyError:
print("error handled")
# all other errors will bubble up here
@handled(my_err_handler())
def read():
i = 0
while i<10:
try:
yield i
i += 1
if i == 3:
raise MyError()
except Exception as e:
# prevent the generator from closing after an Exception
yield e
def process():
for item in read():
print(item)
if __name__=="__main__":
process()
出力:
0
1
2
error handled
3
4
5
6
7
8
9
ただし、これの欠点は、Exception
エラーを生成する可能性のあるジェネレーター内に一般的な処理を配置する必要があることです。ジェネレータで例外を発生させるとクローズされるため、これを回避することはできません。
アイデアのカーネル
yield raise
エラーが発生した後、可能であればジェネレーターが実行を継続できるようにする、ある種のステートメントがあると便利です。次に、次のようなコードを記述できます。
@handled(my_err_handler())
def read():
i = 0
while i<10:
yield i
i += 1
if i == 3:
yield raise MyError()
...そしてhandler()
デコレータは次のようになります:
def handled(handler):
def handled_inner(gen_f):
def wrapper(*args, **kwargs):
g = gen_f(*args, **kwargs)
while True:
try:
g_next = next(g)
except StopIteration:
break
except Exception as e:
handler.throw(e)
else:
yield g_next
return wrapper
handler.send(None) # initialize handler
return handled_inner