我想为生成器编写装饰器,它将捕获for
循环中的所有异常,处理它们并继续循环。
我写了这个装饰器(用于Django 1.5 ORM):
def savepoint_loop(generator, uniq_error_in='_uniq'):
with commit_on_success():
sp = savepoint()
for obj in generator:
try:
yield obj
except DatabaseError as e:
if uniq_error_in not in e.args[0]:
raise
savepoint_rollback(sp)
yield None
else:
savepoint_commit(sp)
sp = savepoint()
我用它像:
loop = savepoint_loop(offer.booking_data.iteritems())
for provider_name, booking_data in loop:
try:
BookingData.objects.create(
offer=pnr_offer, provider=provider_name, **booking_data)
except Exception as e:
loop.throw(e)
但它看起来并不像Pythonic。它允许我让我的代码干,但看起来很混乱。
有没有办法让它更干净?至少我想删除try-except-throw构造或将其更改为with
运算符。
理想情况下,它应如下所示:
for provider_name, booking_data in savepoint_loop(
offer.booking_data.iteritems()):
BookingData.objects.create(
offer=pnr_offer, provider=provider_name, **booking_data)
答案 0 :(得分:1)
import contextlib
@contextlib.contextmanager
def error_processor(uniq_error_in='_uniq'):
sp = savepoint()
try:
yield
except DatabaseError as e:
if uniq_error_in not in e.args[0]:
raise
savepoint_rollback(sp)
else:
savepoint_commit(sp)
这是一个上下文管理器,应该完成你的协同工作,但希望以一种更容易理解的方式。您可以按如下方式使用它:
with commit_on_success():
for provider_name, booking_data in offer.booking_data.iteritems():
with error_processor():
BookingData.objects.create(
offer=pnr_offer, provider=provider_name, **booking_data)
我无法将commit_on_success
放入上下文管理器,因为commit_on_success
需要绕过for
循环,但错误处理需要进入循环内部。 / p>
答案 1 :(得分:0)
在Python for
循环中,步骤2和3封装在for x in y
语句中。但听起来你想使用相同的try...except
来捕获步骤3和4中的异常。所以看起来这将要求你“分解”for
语句,即使用手动实现它改为while
循环。
iterable = offer.booking_data.iteritems() # step 2
try:
while True:
try:
provider_name, booking_data = iterable.next() # step 3
BookingData.objects.create(...) # step 4
except:
except StopIteration:
pass
我确信你会同意这在风格上比你在问题中已有的代码示例更糟糕,try...except
循环中有for
。当然,如果你真的需要步骤3和4进入同一个try
块,可以想象它可能是有用的,但我想这样的情况很少见。
也有可能有一些偷偷摸摸的方式来构建一个能够满足你想要的发电机,但我想不出一个。