try

try 语句可为一组语句指定异常处理器和/或清理代码。语法结构有两种如下。

  • 有 try 和 except 子句(可多个),以及可选的 else 和 finally 子句:
try:
    suite
except expression as identifier:
    suite
else: # 可选
    suite
finally: # 可选
    suite
  • 只有 try 和 finally 子句:
try:
    suite
finally:
    suite

except 子句之后的表达式(通常为异常)expression,关键字 as 以及指定的别名 identifier 都是可选的。

当 try 子句中没有发生异常时,没有异常处理器会被执行。当 try 子句中发生异常时,将启动对异常处理器的搜索。此搜索会依次检查 except 子句,直至找到与该异常相匹配的子句。

except 子句可指定一个或多个异常,如果与发生的异常 “兼容” 则该子句将匹配该异常。

  • 指定的异常如果是发生的异常所属的类或基类,则该子句将匹配该异常;
  • 指定的异常可以置于一个元组,其中包含有与发生的异常 “兼容” 的异常,该子句将匹配该异常。

当一个异常完全未被处理时,解释器会终止程序的执行。

for i in range(3):
    try:
        print(3/i)
    except (ZeroDivisionError, AssertionError) as e:
        print(e)
division by zero
3.0
1.5
for i in range(3):
    try:
        print(3/i)
    except ZeroDivisionError:
        print(f'i={i}引发异常')
i=0引发异常
3.0
1.5
for i in range(3):
    print(3/i)
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-5-ddbcfc1a1b1b> in <module>
      1 for i in range(3):
----> 2     print(3/i)


ZeroDivisionError: division by zero

如果存在无表达式的 except 子句,它必须是最后一个,它将匹配任何异常:

try:
    3/0
except NameError as n:
    print(n)
except:
    pass

如果没有 except 子句与异常相匹配,则会在周边代码和发起调用栈上继续搜索异常处理器,除非存在一个finally 子句正好引发了另一个异常。新引发的异常将导致旧异常的丢失:

def f():
    try:
        return 3/0
    except NameError as n:
        print(n)
try:
    f()
except ZeroDivisionError as z:
    print(z)
division by zero
def f():
    try:
        return 3/0
    except NameError as n:
        print(n)
    finally:
        name1
        
try:
    f()
except ZeroDivisionError as z: # 该异常已丢失
    print(z)
except NameError as n:
    print(n)
name 'name1' is not defined

使用 as 将匹配的异常赋值给一个别名,才能在 except 子句之后引用它,并且它将在 except 子句结束时被清除:

for i in range(3):
    try:
        print(3/i)
    except (ZeroDivisionError, AssertionError) as e:
        print(e)
print(e)
division by zero
3.0
1.5


---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-16-a87e190baccb> in <module>
      4     except (ZeroDivisionError, AssertionError) as e:
      5         print(e)
----> 6 print(e)


NameError: name 'e' is not defined

如果控制流离开 try 子句体时没有引发异常,并且没有执行 return, continue 或 break 语句,可选的 else 子句将被执行:

try:
    print('开始')
except:
    print('捕获')
else:
    print('结束')
开始
结束
while True:    
    try:
        print('开始')
        break
    except:
        print('捕获')
    else:
        print('结束')
开始

如果存在 finally,它用来指定一个 “清理” 处理程序。try,except 或 else 子句中发生任何未处理的异常,会被临时保存。finally 始终被执行,被保存的异常,它会在 finally 子句执行后被重新引发:

try:
    print(3/0)
except:
    name2
else: # 未被执行
    range(3)[5]
finally:
    print('end')
end


---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-24-7d6fe0d94043> in <module>
      1 try:
----> 2     print(3/0)
      3 except:


ZeroDivisionError: division by zero


During handling of the above exception, another exception occurred:


NameError                                 Traceback (most recent call last)

<ipython-input-24-7d6fe0d94043> in <module>
      2     print(3/0)
      3 except:
----> 4     name2
      5 else: # 未被执行
      6     range(3)[5]


NameError: name 'name2' is not defined

如果 finally 子句引发了另一个异常,被保存的异常会被设为新异常的上下文。如果 finally 子句执行了 return, break 或 continue 语句,则被保存的异常会被丢弃:

while True:    
    try:
        print('开始')
    except:
        print('捕获')
    else:
        range(3)[5]
    finally:
        print(3/0)
开始


---------------------------------------------------------------------------

IndexError                                Traceback (most recent call last)

<ipython-input-25-1f272bfbd667> in <module>
      6     else:
----> 7         range(3)[5]
      8     finally:


IndexError: range object index out of range


During handling of the above exception, another exception occurred:


ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-25-1f272bfbd667> in <module>
      7         range(3)[5]
      8     finally:
----> 9         print(3/0)


ZeroDivisionError: division by zero
while True:    
    try:
        print('开始')
    except:
        print('捕获')
    else:
        range(3)[5]
    finally:
        break
开始

当 return, break 或 continue 语句在 finally 语句之前被执行时,finally 子语句也会 ‘在离开时’ 被执行:

while True:    
    try:
        print('开始')
    except:
        print('捕获')
    else:
        break
    finally:
        print('结束')
开始
结束
def f():    
    try:
        return '开始'
    finally:
        print('结束')
f() # 先执行 finally 再离开函数调用
结束


'开始'