Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/visual-studio-code/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在嵌套的try-except-else子句中提高代码的清晰度?_Python_Exception - Fatal编程技术网

Python 如何在嵌套的try-except-else子句中提高代码的清晰度?

Python 如何在嵌套的try-except-else子句中提高代码的清晰度?,python,exception,Python,Exception,有时我会有一系列不同的事情来完成一项任务,例如。G如果我需要得到一条记录,我可以先尝试找到该记录,如果失败,我可以创建丢失的记录,如果失败,我可以使用磁带代替 失败通过抛出代码需要捕获的异常来表示 在Python中,这类似于: 试试看: 记录=查找记录() 除无此记录外: 尝试: 记录=创建_记录() 除CreateFailed外: 记录=磁带 这已经有了堆积压痕的缺点。如果我有五个选项,那么这段代码看起来不太好 但是当try-除了子句之外,还有else子句时,我发现问题更大: 试试看: 记录

有时我会有一系列不同的事情来完成一项任务,例如。G如果我需要得到一条记录,我可以先尝试找到该记录,如果失败,我可以创建丢失的记录,如果失败,我可以使用磁带代替

失败通过抛出代码需要捕获的异常来表示

在Python中,这类似于:

试试看:
记录=查找记录()
除无此记录外:
尝试:
记录=创建_记录()
除CreateFailed外:
记录=磁带
这已经有了堆积压痕的缺点。如果我有五个选项,那么这段代码看起来不太好

但是当
try
-
除了
子句之外,还有
else
子句时,我发现问题更大:

试试看:
记录=查找记录()
除无此记录外:
尝试:
记录=创建_记录()
除CreateFailed外:
记录=磁带
logger.info(“现在使用磁带”)
其他:
logger.info(“创建了新记录”)
其他:
logger.info(“找到记录”)
find_record()
和相应的
record found
消息之间的距离尽可能远,这使得代码很难读取。(将
else
子句的代码直接移动到
try
子句中只是一个选项,前提是该代码肯定不会引发
except
语句中捕获的异常之一,因此这不是通用解决方案。)

同样,这种丑陋随着嵌套级别的增加而变得更糟

有没有更好的方法将其放入Python代码中

  • 在不改变行为和
  • 同时将一个主题的
    try
    except
    子句紧密地放在一起和/或
  • 也许还可以避免堆积嵌套和缩进

  • 你可以把它分解成多个函数

    def handle_missing():
        try:
            record = create_record()
        except CreateFailed:
            record = tape
            logger.info("Using a tape now")
        else:
            logger.info("Created a new record")
        return record
    
    
    def get_record():
        try:
            record = find_record()
        except NoSuchRecord:
            record = handle_missing()
        else:
            logger.info("Record found")
        return record
    
    然后你会像这样使用它

    record = get_record()
    

    您可以使用
    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
    
    如果需要
    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")
    
    请注意,默认情况下的
    磁带
    不是变体的一部分-这是因为它没有故障条件。如果它应该与变量一起执行,则可以添加为
    (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")),
    )
    
    如果元组看起来难以读取,可以使用
    NamedTuple
    来命名元素。这可以与普通元组混合使用:

    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")),
    )
    

    我认为下面的代码更具可读性和简洁性。另外,我确信在实际问题中,我们需要发送一些参数来“find_record”和“create_record”函数,比如id,some,value来创建新记录。工厂解决方案还需要在元组中列出这些参数

    def try\u create(否则返回):
    尝试:
    记录=创建_记录()
    除CreateFailed外:
    记录=否则返回
    logger.info(“现在使用磁带”)
    其他:
    logger.info(“创建了新记录”)
    def try_find(else_call=try_create,**kwargs):
    尝试:
    记录=查找记录()
    除无此记录外:
    尝试创建(**kwargs)
    其他:
    logger.info(“找到记录”)
    try\u find(else\u call=try\u create,else\u return=tape)
    
    将其分为两个功能?您是否承诺使用异常来表示异常的功能结果?如果<代码> FordOrthys< /Cord>和<代码> CREATETIORION < /COD>返回<代码> No.<代码>,而不是引发异常,这可能会为代码块打开一些设计可能性。如果有相关的故障条件,您可能会考虑创建自定义ErrorHandler。我不认为有一个一般的答案。设计将完全取决于每个函数返回和/或可以产生的内容。@chepner我经常碰到这个问题,所以我希望有一个通用的方法,而不是只在一种情况下工作的专门方法。要捕获的异常可能是诸如
    索引器
    值错误
    之类的一般性事件,因此,我希望尽量减少
    try
    -子句,以避免无意中捕获其他内容。您可以使用第三个工厂
    lambda:tape
    ,它消除了
    for
    循环中
    else
    子句的需要。一般情况下,当然可能会引发问题,但实际上,您可以通过
    ()
    作为
    语句的值,除了
    之外,它什么也抓不到。@chepner,这里有一个答案:)为什么你仍然说它无法回答?实际上我喜欢这个。如果工厂很复杂,我想我会把它们分解成单个函数(比如@hansolo)。如果不同情况下的代码应该有一些内部函数的副作用,如设置局部变量等,这只会带来麻烦。但是,这些都需要输入到返回值中。@MisterMiyagi同样,如果要在
    for
    循环中添加
    else
    子句,您不必从
    result=tape
    开始;把它也放在
    else
    子句中。是的,我明白了,它将每个缩进级别分解为一个函数:)这似乎很简单,是的。是的,如果你看到自己编写
    try..except..else
    嵌套,你几乎总是可以将其分解为多个函数,代码将更具可读性,更易于维护:)