写点什么

Python 基础之:Python 中的异常和错误

发布于: 2021 年 03 月 29 日

简介

和其他的语言一样,Python 中也有异常和错误。在 Python 中,所有异常都是 BaseException 的类的实例。 今天我们来详细看一下 Python 中的异常和对他们的处理方式。


Python 中的内置异常类

Python 中所有异常类都来自 BaseException,它是所有内置异常的基类。


虽然它是所有异常类的基类,但是对于用户自定义的类来说,并不推荐直接继承 BaseException,而是继承 Exception.


先看下 Python 中异常类的结构关系:


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           +-- ResourceWarning
复制代码

其中 BaseExceptionExceptionArithmeticErrorBufferErrorLookupError 主要被作为其他异常的基类。


语法错误

在 Python 中,对于异常和错误通常可以分为两类,第一类是语法错误,又称解析错误。也就是代码还没有开始运行,就发生的错误。


其产生的原因就是编写的代码不符合 Python 的语言规范:


>>> while True print('Hello world')  File "<stdin>", line 1    while True print('Hello world')                   ^SyntaxError: invalid syntax
复制代码

上面代码原因是 print 前面少了 冒号。


异常

即使我们的程序符合 python 的语法规范,但是在执行的时候,仍然可能发送错误,这种在运行时发送的错误,叫做异常。


看一下下面的异常:


>>> 10 * (1/0)Traceback (most recent call last):  File "<stdin>", line 1, in <module>ZeroDivisionError: division by zero>>> 4 + spam*3Traceback (most recent call last):  File "<stdin>", line 1, in <module>NameError: name 'spam' is not defined>>> '2' + 2Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: Can't convert 'int' object to str implicitly
复制代码

异常处理

程序发生了异常之后该怎么处理呢?


我们可以使用 try except 语句来捕获特定的异常。


>>> while True:...     try:...         x = int(input("Please enter a number: "))...         break...     except ValueError:...         print("Oops!  That was no valid number.  Try again...")...
复制代码

上面代码的执行流程是,首先执行 try 中的子语句,如果没有异常发生,那么就会跳过 except,并完成 try 语句的执行。


如果 try 中的子语句中发生了异常,那么将会跳过 try 子句中的后面部分,进行 except 的异常匹配。如果匹配成功的话,就会去执行 except 中的子语句。


如果发生的异常和 except 子句中指定的异常不匹配,则将其传递到外部的 try语句中。


一个 try 中可以有多个 except 子句,我们可以这样写:


    try:        raise cls()    except D:        print("D")    except C:        print("C")    except B:        print("B")
复制代码

一个 except 也可以带多个异常:


... except (RuntimeError, TypeError, NameError):...     pass
复制代码

except 子句还可以省略异常名,用来匹配所有的异常:


import sys
try: f = open('myfile.txt') s = f.readline() i = int(s.strip())except OSError as err: print("OS error: {0}".format(err))except ValueError: print("Could not convert data to an integer.")except: print("Unexpected error:", sys.exc_info()[0]) raise
复制代码


try … except语句有一个可选的 else 子句,在使用时必须放在所有的 except 子句后面。对于在 try 子句不引发异常时必须执行的代码来说很有用。 例如:


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 ,这个变量代表这个异常实例。


我们可以通过 instance.args 来输出异常的参数。


同时,因为异常实例定义了 __str__(),所以可以直接使用 print 来输出异常的参数。而不需要使用 .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)...<class 'Exception'>('spam', 'eggs')('spam', 'eggs')x = spamy = eggs
复制代码

上面的例子中,我们在 try 字句中抛出了一个异常,并且指定了 2 个参数。


抛出异常

我们可以使用 raise 语句来抛出异常。


>>> raise NameError('HiThere')Traceback (most recent call last):  File "<stdin>", line 1, in <module>NameError: HiThere
复制代码

raise 的参数是一个异常,这个异常可以是异常实例或者是一个异常类。


注意,这个异常类必须是Exception的子类。


如果传递的是一个异常类,那么将会调用无参构造函数来隐式实例化:


raise ValueError  # shorthand for 'raise ValueError()'
复制代码

如果我们捕获了某些异常,但是又不想去处理,那么可以在 except 语句中使用 raise,重新抛出异常。


>>> try:...     raise NameError('HiThere')... except NameError:...     print('An exception flew by!')...     raise...An exception flew by!Traceback (most recent call last):  File "<stdin>", line 2, in <module>NameError: HiThere
复制代码

异常链

如果我们通过 except 捕获一个异常 A 之后,可以通过 raise 语句再次抛出一个不同的异常类型 B。


那么我们看到的这个异常信息就是 B 的信息。但是我们并不知道这个异常 B 是从哪里来的,这时候,我们就可以用到异常链。


异常链就是抛出异常的时候,使用 raise from 语句:


>>> def func():...     raise IOError...>>> try:...     func()... except IOError as exc:...     raise RuntimeError('Failed to open database') from exc...Traceback (most recent call last):  File "<stdin>", line 2, in <module>  File "<stdin>", line 2, in funcOSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File "<stdin>", line 4, in <module>RuntimeError: Failed to open database
复制代码

上面的例子中,我们在捕获 IOError 之后,又抛出了 RuntimeError,通过使用异常链,我们很清晰的看出这两个异常之间的关系。


默认情况下,如果异常是从 except 或者 finally 中抛出的话,会自动带上异常链信息。


如果你不想带上异常链,那么可以 from None 。


try:    open('database.sqlite')except IOError:    raise RuntimeError from None
Traceback (most recent call last): File "<stdin>", line 4, in <module>RuntimeError
复制代码

自定义异常

用户可以继承 Exception 来实现自定义的异常,我们看一些自定义异常的例子:


class Error(Exception):    """Base class for exceptions in this module."""    pass
class InputError(Error): """Exception raised for errors in the input.
Attributes: expression -- input expression in which the error occurred message -- explanation of the error """
def __init__(self, expression, message): self.expression = expression self.message = message
class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed.
Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """
def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
复制代码

finally

try 语句可以跟着一个 finally 语句来实现一些收尾操作。


>>> try:...     raise KeyboardInterrupt... finally:...     print('Goodbye, world!')...Goodbye, world!KeyboardInterruptTraceback (most recent call last):  File "<stdin>", line 2, in <module>
复制代码

finally 子句将作为 try 语句结束前的最后一项任务被执行, 无论 try 中是否产生异常,finally 语句中的代码都会被执行。


如果 finally 子句中包含一个 return 语句,则返回值将来自 finally 子句的某个 return 语句的返回值,而非来自 try 子句的 return 语句的返回值。


>>> def bool_return():...     try:...         return True...     finally:...         return False...>>> bool_return()False
复制代码

本文已收录于 http://www.flydean.com/09-python-error-exception/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!


发布于: 2021 年 03 月 29 日阅读数: 9
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
Python基础之:Python中的异常和错误