异常与异常处理

Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获)时,程序将终止并显示一条错误消息(traceback)。

异常和函数有着天然的联系。如果不处理函数中引发的异常,它将向上传播到调用函数的地方。如果在那里也未得到处理,异常将继续传播,直至到达主程序(全局作用域)。如果主程序中也没有异常处理程序,程序将终止并显示栈跟踪消息。



触发异常:raise

raise 唯一的参数就是要触发的异常。这个参数必须是异常实例或异常类(派生自 Exception 类)。

raise Exception

# output:
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 1, in <module>
#     raise Exception
# Exception
raise Exception('Exception Message')

# output:
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 1, in <module>
#     raise Exception('Exception Message')
# Exception: Exception Message

如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常:

try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    raise

# output:
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 2, in <module>
#     raise NameError('HiThere')
# NameError: HiThere
# An exception flew by!

异常链

raise 语句支持可选的 from 子句,该子句用于启用链式异常。

# exc must be exception instance or None.
raise RuntimeError from exc

转换异常时,这种方式很有用。例如:

def func():
    raise ConnectionError


try:
    func()
except ConnectionError as exc:
    raise RuntimeError('Failed to open database') from exc

# output:
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 6, in <module>
#     func()
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 2, in func
#     raise ConnectionError
# ConnectionError
# 
# The above exception was the direct cause of the following exception:
# 
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 8, in <module>
#     raise RuntimeError('Failed to open database') from exc
# RuntimeError: Failed to open database

异常链会在 except 或 finally 子句内部引发异常时自动生成。 这可以通过使用 from None 这样的写法来禁用:

try:
    open('database.sqlite')
except OSError:
    raise RuntimeError from None

# output:
# Traceback (most recent call last):
#   File "E:\PyCode\Python_Study\LsitDemo.py", line 4, in <module>
#     raise RuntimeError from None
# RuntimeError

内置异常

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- EncodingWarning
           +-- ResourceWarning

自定义异常类

直接或间接地继承Exception

class SomeCustomException(Exception): pass
# pass需要更改为所需的代码

异常的处理

try 语句的工作原理如下:

  • 首先,执行 try 子句 (try 和 except 关键字之间的(多行)语句)。

  • 如果没有触发异常,则跳过 except 子句try 语句执行完毕。

  • 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。 如果异常的类型与 except 关键字后指定的异常相匹配,则会执行 except 子句,然后跳到 try/except 代码块之后继续执行。

  • 如果发生的异常与 except 子句 中指定的异常不匹配,则它会被传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常 且执行将终止并输出如上所示的消息。

try 语句可以有多个 except 子句 来为不同的异常指定处理程序。 但最多只有一个处理程序会被执行。 处理程序只处理对应的 try 子句 中发生的异常,而不处理同一 try 语句内其他处理程序中的异常。 except 子句 可以用带圆括号的元组来指定多个异常,例如:

except (RuntimeError, TypeError, NameError):
pass
# pass需要更改为所需的代码

如果发生的异常与 except 子句中的类是同一个类或是它的基类时,则该类与该异常相兼容(反之则不成立 --- 列出派生类的 except 子句 与基类不兼容)。

例如,下面的代码将依次打印 B, C, D,

class B(Exception):
    pass


class C(B):
    pass


class D(C):
    pass


for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

# output: B
# output: C
# output: D

请注意如果颠倒 except 子句 的顺序(把 except B 放在最前),则会输出 B, B, B --- 即触发了第一个匹配的 except 子句。请注意如果颠倒 except 子句 的顺序(把 except B 放在最前),则会输出 B, B, B --- 即触发了第一个匹配的 except 子句

class B(Exception):
    pass


class C(B):
    pass


class D(C):
    pass


for cls in [B, C, D]:
    try:
        raise cls()
    except B:
        print("B")
    except C:
        print("C")
    except D:
        print("D")


# output: B
# output: B
# output: B

如果你就是要使用一段代码捕获所有的异常,只需在except语句中不指定任何异常类即可。

可以选择让最后一个 except 子句省略异常名称,但在此之后异常值必须从 sys.exc_info()获取。

try ... except 语句具有可选的 else 子句,该子句如果存在,它必须放在所有 except 子句 之后它适用于 try 子句 没有引发异常但又必须要执行的代码。使用 else 子句比向 try 子句添加额外的代码要好,可以避免意外捕获非 try ... except 语句保护的代码触发的异常。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

except 子句 可以在异常名称后面指定一个变量。 这个变量会绑定到一个异常实例并将参数存储在 instance.args 中。

为了方便起见,该异常实例定义了 __str__() 以便能直接打印参数而无需引用 .args。 也可以在引发异常之前先实例化一个异常并根据需要向其添加任何属性。:

try:
    raise Exception('spam', 'eggs')
except Exception as inst:
    print(type(inst))    # the exception instance
    print(inst.args)     # arguments stored in .args
    print(inst)          # __str__ allows args to be printed directly,
                         # but may be overridden in exception subclasses
    x, y = inst.args     # unpack args
    print('x =', x)
    print('y =', y)

# output: <class 'Exception'>
# output: ('spam', 'eggs')
# output: ('spam', 'eggs')
# output: x = spam
# output: y = eggs

如果异常有参数,则它们将作为未处理异常的消息的最后一部分('详细信息')打印。

def this_fails():
    x = 1/0

try:
    this_fails()
except ZeroDivisionError as err:
    print('Handling run-time error:', err)

# output: Handling run-time error: division by zero

定义清理操作

try 语句还有一个可选子句,用于定义在所有情况下都必须要执行的清理操作。

如果存在 finally 子句,则 finally 子句是 try 语句结束前执行的最后一项任务。不论 try 语句是否触发异常,都会执行 finally 子句。以下内容介绍了几种比较复杂的触发异常情景:

  • 如果执行 try 子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有 except 子句处理,在 finally 子句执行后会被重新触发。

  • except 或 else 子句执行期间也会触发异常。 同样,该异常会在 finally 子句执行之后被重新触发。

  • 如果 finally 子句中包含 breakcontinue 或 return 等语句,异常将不会被重新引发。

  • 如果执行 try 语句时遇到 break,、continue 或 return 语句,则 finally 子句在执行 breakcontinue 或 return 语句之前执行。

  • 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值