连续 except、嵌套 try、元组异常与 if else 代码块之间异常处理的功能差异

Functional difference in exception handling between consecutive except, nested try, tuple exception with if else code blocks

代码块 1、2、3 中的异常处理之间是否存在功能差异?我想根据类型错误打印不同的消息,例如,如果 psycopg2 错误,则包括错误代码。我读到 nested try except blocks 是很好的做法。我以 psycopg2 为例。

# code block 1
def my_func(fun_arg):
    try:
        # ... some stuff
    except psycopg2.Error as error:
        print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
    except Exception as error:
        print(f'error in my_func {type(error).__name__} {error.args}')
# code block 2
def my_func(fun_arg):
    try:
        # ... some stuff
        try:
            # ... some stuff
        except psycopg2.Error as error:
            print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
    except Exception as error:
        print(f'error in my_func {type(error).__name__} {error.args}')
# code block 3
def my_func(fun_arg):
    try:
        # ... some stuff
    except (psycopg2.Error, Exception) as error:
        if (type(error).__name__ in (
    'DatabaseError', 'OperationalError', 'NotSupportedError', 
    'ProgrammingError', 'DataError','IntegrityError',))
            print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
        else:
            print(f'error in my_func {type(error).__name__} {error.args}')

我认为前两个选项都可以。

就我个人而言,我认为第一个更容易阅读一个简短的简单函数,尽管如果 my_func 更大且嵌套更复杂,那么我会选择第二个选项,因为它清楚地说明了确切的位置其中 sycopg2.Error 可能会被提高。

他们不会使用第三个选项。如果你想捕获 multiple exceptions 使用这个语法:

except (RuntimeError, TypeError, NameError):
    pass

在您的情况下,没有功能差异。在这种情况下,您可以按照 Zen of Python 选择最合适的。有很多方法可以解释它,但我会选择第 1 块,因为它是最简单、最平坦且最易读的。

出于上述原因,第一个区块是首选区块。

当内部 try 的所有分支都可以引发相同的异常并且异常的处理程序也相同时,需要第二个块。例如:

try:
    try:
        store_to_db(value)
    except ValueError:
        store_to_db(fallback)
except DbError:
    log_error()

第三块只是比第一块更复杂、更 difficult-to-read 的替代方案。除了两个异常都有共享代码外,我无法想象它的实际用例。例如:

try:
    do_something()
except (DatabaseError, OperationalError, KeyError) as error:
    data = collect_some_data()
    if type(error).__name__ in ('DatabaseError', 'OperationalError'):
        print(f"Error while inserting data to PostgreSQL {data}")
    else:
        print(f'error in my_func {data}')

尽管 isinstance 可能优于 type(error).__name__ ==。我仍然认为最好将其拆分为多个 except 子句并重复调用 collect_some_data 但这是个人喜好问题。

要考虑的另一件事是,通常首选“将 try 子句限制为必要的绝对最小代码量,因为它可以避免掩盖错误”[PEP 8]。这是尽可能避免嵌套 try 的另一个原因。