有时我会尝试各种不同的事情来尝试完成一项任务,例如,如果需要获取记录,我可以先尝试查找记录,如果失败,则可以创建丢失的记录,并且如果失败也可以,我可以改用磁带。
失败是通过抛出我的代码需要捕获的异常来表示的。
在Python中,这看起来像这样:
try:
record = find_record()
except NoSuchRecord:
try:
record = create_record()
except CreateFailed:
record = tape
Run Code Online (Sandbox Code Playgroud)
这已经具有堆积凹痕的缺点。如果我有五个选择,那么这段代码看起来不太好。
但是,当- 子句中也有else子句时,我会发现问题更加严重:tryexcept
try:
record = find_record()
except NoSuchRecord:
try:
record = create_record()
except CreateFailed:
record = tape
logger.info("Using a tape now")
else:
logger.info("Created a new record")
else:
logger.info("Record found")
Run Code Online (Sandbox Code Playgroud)
的find_record()和相应的Record found消息是尽可能远离可能这使得它很难读代码。(将else子句的代码直接移到try子句中只是一种选择,如果该代码绝对不会引发except语句中捕获的异常之一,那么这不是一般的解决方案。)
同样,每增加一层嵌套,这种丑陋就变得更糟。
有没有更好的方法将其放入Python代码
try和except子句保持在一起和/或您可以使用for循环来依次尝试变体:
for task, error in ((find_record, NoSuchRecord), (create_record, CreateFailed)):
try:
result = task()
except error:
continue
else:
break
else:
# for..else is only entered if there was no break
result = tape
Run Code Online (Sandbox Code Playgroud)
如果需要else子句,可以将其作为单独的函数提供:
for task, error, success in (
(find_record, NoSuchRecord, lambda: logger.info("Record found")),
(create_record, CreateFailed, lambda: logger.info("Created a new record"))
):
try:
result = task()
except error:
continue
else:
success()
break
else:
result = tape
logger.info("Using a tape now")
Run Code Online (Sandbox Code Playgroud)
注意,默认情况下tape是不会变体的一部分-这是因为它没有失败的情况。如果应使用变体执行,则可以将其添加为(lambda: tape, (), lambda: None)。
您可以将所有这些放到一个函数中以供重用:
def try_all(*cases):
for task, error, success in cases:
try:
result = task()
except error:
continue
else:
success()
return result
try_all(
(find_record, NoSuchRecord, lambda: logger.info("Record found")),
(create_record, CreateFailed, lambda: logger.info("Created a new record")),
(lambda: tape, (), lambda: logger.info("Using a tape now")),
)
Run Code Online (Sandbox Code Playgroud)
如果元组看起来难以阅读,NamedTuple则可以使用a来命名元素。这可以与普通元组混合:
from typing import NamedTuple, Callable, Union, Tuple
from functools import partial
class Case(NamedTuple):
task: Callable
error: Union[BaseException, Tuple[BaseException, ...]]
success: Callable
try_all(
Case(
task=find_record,
error=NoSuchRecord,
success=partial(logger.info, "Record found")),
(
create_record, CreateFailed,
partial(logger.info, "Created a new record")),
Case(
task=lambda: tape,
error=(),
success=partial(logger.info, "Using a tape now")),
)
Run Code Online (Sandbox Code Playgroud)