更多 pythonic 方式来处理嵌套的 try... 除了块?
More pythonic way to handle nested try... except blocks?
是否有更简洁或更 pythonic 的方法来执行以下操作?
try:
error_prone_function(arg1)
except MyError:
try:
error_prone_function(arg2)
except MyError:
try:
another_error_prone_function(arg3)
except MyError:
try:
last_error_prone_function(arg4)
except MyError:
raise MyError("All backup parameters failed.")
基本上是:如果尝试#1 失败,请尝试#2。如果#2 失败,请尝试#3。如果#3 失败,请尝试#4。如果#4 失败,...如果#n 失败,则最后引发一些异常。
请注意,我不一定每次都调用相同的函数,也不一定每次都使用相同的函数参数。然而,我是,期望每个函数都有相同的异常MyError
。
感谢 John Kugelman 的 post here,我决定使用它,它利用 for 循环的鲜为人知的 else
子句在整个列表具有时执行代码没有发生 break
就已经筋疲力尽了。
funcs_and_args = [(func1, "150mm"),
(func1, "100mm",
(func2, "50mm"),
(func3, "50mm"),
]
for func, arg in funcs_and_args :
try:
func(arg)
# exit the loop on success
break
except MyError:
# repeat the loop on failure
continue
else:
# List exhausted without break, so there must have always been an Error
raise MyError("Error text")
正如 Daniel Roseman 在下面评论的那样,注意缩进,因为 try
语句 也 有一个 else
子句。
基于生成器的方法可能比数据驱动的方法更灵活:
def attempts_generator():
# try:
# <the code you're attempting to run>
# except Exception as e:
# # failure
# yield e.message
# else:
# # success
# return
try:
print 'Attempt 1'
raise Exception('Failed attempt 1')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 2'
# raise Exception('Failed attempt 2')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 3'
raise Exception('Failed attempt 3')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 4'
raise Exception('Failed attempt 4')
except Exception as e:
yield e.message
else:
return
raise Exception('All attempts failed!')
attempts = attempts_generator()
for attempt in attempts:
print attempt + ', retrying...'
print 'All good!'
我们的想法是构建一个生成器,该生成器通过重试循环遍历尝试块。
一旦生成器成功尝试,它就会停止自己的迭代并硬性 return
。不成功的尝试屈服于下一个回退的重试循环。否则,如果尝试次数用完,它最终会抛出无法恢复的错误。
这里的优点是 try..excepts 的内容可以是您想要的任何内容,而不仅仅是单独的函数调用(无论出于何种原因特别尴尬)。生成器函数也可以在闭包中定义。
就像我在这里所做的那样,yield 也可以传回用于日志记录的信息。
上面的输出,顺便说一句,注意我让尝试 2 成功,如所写:
mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
All good!
如果您取消注释尝试 2 中的加注,那么它们都会失败,您将得到:
mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
Failed attempt 2, retrying...
Attempt 3
Failed attempt 3, retrying...
Attempt 4
Failed attempt 4, retrying...
Traceback (most recent call last):
File "./fallback.py", line 47, in <module>
for attempt in attempts:
File "./fallback.py", line 44, in attempts_generator
raise Exception('All attempts failed!')
Exception: All attempts failed!
编辑:
就您的伪代码而言,这看起来像:
def attempts_generator():
try:
error_prone_function(arg1)
except MyError
yield
else:
return
try:
error_prone_function(arg2)
except MyError
yield
else:
return
try:
another_error_prone_function(arg3)
except MyError:
yield
else:
return
try:
last_error_prone_function(arg4)
except MyError:
yield
else:
return
raise MyError("All backup parameters failed.")
attempts = attempts_generator()
for attempt in attempts:
pass
它会让除 MyError 之外的任何异常冒出并停止整个事情。您还可以选择为每个块捕获不同的错误。
是否有更简洁或更 pythonic 的方法来执行以下操作?
try:
error_prone_function(arg1)
except MyError:
try:
error_prone_function(arg2)
except MyError:
try:
another_error_prone_function(arg3)
except MyError:
try:
last_error_prone_function(arg4)
except MyError:
raise MyError("All backup parameters failed.")
基本上是:如果尝试#1 失败,请尝试#2。如果#2 失败,请尝试#3。如果#3 失败,请尝试#4。如果#4 失败,...如果#n 失败,则最后引发一些异常。
请注意,我不一定每次都调用相同的函数,也不一定每次都使用相同的函数参数。然而,我是,期望每个函数都有相同的异常MyError
。
感谢 John Kugelman 的 post here,我决定使用它,它利用 for 循环的鲜为人知的 else
子句在整个列表具有时执行代码没有发生 break
就已经筋疲力尽了。
funcs_and_args = [(func1, "150mm"),
(func1, "100mm",
(func2, "50mm"),
(func3, "50mm"),
]
for func, arg in funcs_and_args :
try:
func(arg)
# exit the loop on success
break
except MyError:
# repeat the loop on failure
continue
else:
# List exhausted without break, so there must have always been an Error
raise MyError("Error text")
正如 Daniel Roseman 在下面评论的那样,注意缩进,因为 try
语句 也 有一个 else
子句。
基于生成器的方法可能比数据驱动的方法更灵活:
def attempts_generator():
# try:
# <the code you're attempting to run>
# except Exception as e:
# # failure
# yield e.message
# else:
# # success
# return
try:
print 'Attempt 1'
raise Exception('Failed attempt 1')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 2'
# raise Exception('Failed attempt 2')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 3'
raise Exception('Failed attempt 3')
except Exception as e:
yield e.message
else:
return
try:
print 'Attempt 4'
raise Exception('Failed attempt 4')
except Exception as e:
yield e.message
else:
return
raise Exception('All attempts failed!')
attempts = attempts_generator()
for attempt in attempts:
print attempt + ', retrying...'
print 'All good!'
我们的想法是构建一个生成器,该生成器通过重试循环遍历尝试块。
一旦生成器成功尝试,它就会停止自己的迭代并硬性 return
。不成功的尝试屈服于下一个回退的重试循环。否则,如果尝试次数用完,它最终会抛出无法恢复的错误。
这里的优点是 try..excepts 的内容可以是您想要的任何内容,而不仅仅是单独的函数调用(无论出于何种原因特别尴尬)。生成器函数也可以在闭包中定义。
就像我在这里所做的那样,yield 也可以传回用于日志记录的信息。
上面的输出,顺便说一句,注意我让尝试 2 成功,如所写:
mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
All good!
如果您取消注释尝试 2 中的加注,那么它们都会失败,您将得到:
mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
Failed attempt 2, retrying...
Attempt 3
Failed attempt 3, retrying...
Attempt 4
Failed attempt 4, retrying...
Traceback (most recent call last):
File "./fallback.py", line 47, in <module>
for attempt in attempts:
File "./fallback.py", line 44, in attempts_generator
raise Exception('All attempts failed!')
Exception: All attempts failed!
编辑:
就您的伪代码而言,这看起来像:
def attempts_generator():
try:
error_prone_function(arg1)
except MyError
yield
else:
return
try:
error_prone_function(arg2)
except MyError
yield
else:
return
try:
another_error_prone_function(arg3)
except MyError:
yield
else:
return
try:
last_error_prone_function(arg4)
except MyError:
yield
else:
return
raise MyError("All backup parameters failed.")
attempts = attempts_generator()
for attempt in attempts:
pass
它会让除 MyError 之外的任何异常冒出并停止整个事情。您还可以选择为每个块捕获不同的错误。