目录
-何为异常
-异常的分类
-异常的处理
-自定义异常和 raise 语句
-assert 断言
何为异常
异常即是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。 一般情况下,在 Python 无法正常处理程序时就会发生一个异常;也可以利用关键字 raise 人为的抛出一个异常。在 Python 中,异常也是一种对象,表示一个错误。当 Python 程序发生异常时我们需要捕获并处理它,否则就可能会产生不可预料的后果,甚至是程序的运行终止。
异常的分类
异常即是一种错误,而错误大体上可以分为两类:1、语法错误;这种错误是很容易发现和避免的。因为Python解释器会在程序编译之前进行语法检查,如果有语法错误,程序就不会被执行。2、逻辑错误;这种错误是一般在程序执行过程中才会被发现,当然,经验丰富的程序员在编写或读代码的过程中就能避免、发现很多的逻辑错误,但逻辑错误是很难避免的,比如你变量名打错了一个字母,函数传入的参数类型不符合要求等等,都会引起逻辑错误。
几种常见的异常类别
AttributeError # 试图访问一个对象没有的属性,比如sample.x,但是sample没有x这个属性
TypeError # 传入对象类型与要求的不符合
KeyError # 试图访问字典里不存在的键
IndexError # 下标索引超出序列边界,比如当List只有三个元素,却试图访问List[5]
NameError # 使用一个还未被赋予对象的变量
IOError # 输入/输出异常;一般就是文件无法打开
ImportError # 无法引入模块或包;一般是路径问题或名称错误
IndexError # 下标索引超出序列边界,比如当List只有三个元素,却试图访问List[5]
KeyboardInterrupt # 用户中断执行,即Ctrl+C被按下
UnboundLocalError #试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
ValueError # 传入一个调用者不期望的值,即使值的类型是正确的
IndentationError # 语法错误(的子类),当代码没有正确对齐时引发
SyntaxError # Python代码非法,代码不能编译
内置异常的层次结构及解释(全)
BaseException # 所有内置异常的基类
+-- SystemExit # 解释器请求退出,sys.exit()函数引发此异常
+-- KeyboardInterrupt # 当用户按下中断键(通常是Ctrl+c或delete)时引发
+-- GeneratorExit # 生成器(generator)发生异常来通知退出;关闭协程时也会引发
+-- Exception # 所有内置的,非系统退出的异常都派生自此类。所有用户定义的异常也应从此类派生。
+-- StopIteration # 迭代器没有更多的值
+-- StopAsyncIteration # 必须通过异步迭代器对象的__anext__()方法 引发以停止迭代
+-- ArithmeticError # 所有数值计算错误的基类
| +-- FloatingPointError # 浮点计算错误
| +-- OverflowError # 数值运算超出最大限制
| +-- ZeroDivisionError # 当除法或模运算的第二个参数为零时引发
+-- AssertionError # 断言语句失败
+-- AttributeError # 当属性引用或分配失败是引发
+-- BufferError # 当无法执行与缓冲区相关的操作时引发
+-- EOFError # 当input()函数达到文件结束条件(EOF)而不读取任何数据时引发。(注意:io.IOBase.read()和 io.IOBase.readline()方法在到达EOF时返回空字符串。)
+-- ImportError # 导入模块/包/对象失败
| +-- ModuleNotFoundError # 无法找到模块时引发
+-- LookupError # 无效数据查询的基类
| +-- IndexError # 序列中没有此索引(index)时引发
| +-- KeyError # 映射中没有这个键时引发
+-- MemoryError # 内存溢出时引发(对于Python 解释器不是致命的,可以通过删除一些对象挽救)
+-- NameError # 找不到变量名时引发
| +-- UnboundLocalError # 访问未初始化的局部变量
+-- OSError # 当系统功能返回与系统相关的错误时引发
| +-- BlockingIOError # 当某个操作将在设置为非阻塞操作的对象(例如套接字)上阻塞时引发
| +-- ChildProcessError # 在子进程上的操作失败时引发
| +-- ConnectionError # 连接相关问题的基类。
| | +-- BrokenPipeError # 试图在另一端已关闭的情况下在管道上进行写操作或试图在已关闭以进行写操作的套接字上进行写操作时引发
| | +-- ConnectionAbortedError # 当对等方中止连接尝试时引发
| | +-- ConnectionRefusedError # 当对等方拒绝连接尝试时引发
| | +-- ConnectionResetError # 当对等方重置连接时引发
| +-- FileExistsError # 在尝试创建已经存在的文件或目录时引发
| +-- FileNotFoundError # 在请求文件或目录但不存在时引发
| +-- InterruptedError # 当系统调用被传入信号中断时引发
| +-- IsADirectoryError # 在目录上请求文件操作时引发(如 os.remove())
| +-- NotADirectoryError # 在非目录上请求目录操作时引发(如 os.listdir())
| +-- PermissionError # 尝试在没有足够访问权限的情况下运行操作时引发,例如文件系统权限
| +-- ProcessLookupError # 在给定进程不存在时引发
| +-- TimeoutError # 当系统功能在系统级别超时时引发
+-- ReferenceError # 当由该weakref.proxy()函数创建的弱引用代理用于垃圾回收后,用于访问引用对象的属性时,会引发此异常
+-- RuntimeError # 在检测到不属于任何其他类别的错误时引发
| +-- NotImplementedError # 在用户定义的基类中,当抽象方法要求派生类覆盖该方法时,或者在开发该类以指示仍需要添加实际实现时,应引发此异常
| +-- RecursionError # 当解释器检测到sys.getrecursionlimit()超过最大递归深度时引发
+-- SyntaxError # 在解析器遇到语法错误时引发
| +-- IndentationError # 与缩进不正确相关的语法错误的基类
| +-- TabError # 当缩进包含制表符和空格的不一致使用时引发
+-- SystemError # 在解释器发现内部错误时引发
+-- TypeError # 在将操作或功能应用于不合适类型的对象时引发
+-- ValueError # 当操作或函数接收到类型正确但值不合适的参数时引发,并且这种情况不能引发更精确的异常
| +-- UnicodeError # 在Unicode相关的编码或解码错误发生时引发
| +-- UnicodeDecodeError # 在解码期间发生与Unicode相关的错误时引发
| +-- UnicodeEncodeError # 在编码过程中发生与Unicode相关的错误时引发
| +-- UnicodeTranslateError # 在翻译过程中发生Unicode相关错误时引发
+-- Warning # 警告类别的基类
+-- DeprecationWarning # 当有关警告已用于其他Python开发人员时,这些警告的基类
+-- PendingDeprecationWarning # 有关已过时且预期在将来不推荐使用但现在不推荐使用的功能的警告的基类
+-- RuntimeWarning # 警告有关可疑运行时行为的基类
+-- SyntaxWarning # 警告有关可疑语法的基类
+-- UserWarning # 用户代码生成的警告的基类
+-- FutureWarning # 当这些警告旨在供使用Python编写的应用程序的最终用户使用时,这些警告的基类
+-- ImportWarning # 用于警告有关模块导入中可能错误的警告的基类
+-- UnicodeWarning # 与Unicode相关的警告的基类
+-- BytesWarning # 与bytes和bytearray相关的警告的基类
+-- ResourceWarning # 与资源使用相关的警告的基类
异常的处理
为了程序的健壮性与容错性,即保证在遇到错误时程序不会崩溃,我们需要对异常进行处理。如果错误发生的条件是可预知的,我们可以通过 if 语句进行处理,即在错误发生之前进行预防。如果错误发生的条件是不可预知的,就需要用try...except:
语句在错误发生之后进行处理。
# 例如下
try:
# try 下的代码组应该是可能产生异常的语句
# age = 10
print(age)
except NameError as error:
'''
except 下的代码组是对捕获异常的处理
except 后面可以跟一类异常,如:except Exception: 表示会捕获 Exception 所有的子类异常
也可以跟一个或多个异常,多个异常需用元组的形式列出,如: except (NameError, TypeError):
还可以给异常起一个别名,如本例
另 except 后面也可以不跟具体的异常类型,如:except: 这表示将捕获所有类型的异常
'''
print(error)
# 输出异常的值
# 异常一般包含三个信息:异常的值,异常的类型和异常的追溯信息
else:
print('没有产生异常')
finally:
print('无论有没有异常,这里都是要执行的')
# 输出如下:
# name 'age' is not defined
# 无论有没有异常,这里都是要执行的
PS:上例中的 else 和 finally 是可选的
try 的工作原理是:当开始一个 try 语句后,python 就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try 子句先执行,接下来会发生什么依赖于执行时是否出现异常。
如果当 try 后的语句执行时发生异常,python 就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。 如果没有匹配的 except 子句,异常将被递交到上层的 try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。
如果在 try 子句执行时没有发生异常,python 将执行 else 或 finally(如果有else或 finally的话)后的语句,然后控制流通过整个try语句。
自定义异常和 raise 语句
尽管内置异常已经很丰富了,但有时候还是不能完全满足我们的需求。这时候我们就需要自定义一个异常!
自定义异常需要知道一下内容:
1、异常的本质是一个类,且它们都是 BaseException 的子类。
2、所有内置的,非系统退出的异常都派生自 Exception 类,用户定义的异常也应从此类派生
3、直接或间接继承了 Exception 的类,就是一种自定义的异常类,类名即异常名
# 自定义一个类,这是一个模板
class AgeError(Exception):
def __init__(self, err_info):
super().__init__(self) # 调用父类的构造方法
self.err_info = err_info
def __str__(self):
return self.err_info
OK,现在我们已经创建了一个自定义的异常了,那么我们怎么抛出这个异常呢?答案是利用关键字:raise
# 例如下
class AgeError(Exception):
def __init__(self, err_info):
super().__init__(self) # 调用父类的构造方法
self.err_info = err_info
def __str__(self):
return self.err_info
try:
age = int(input())
if age < 0:
raise AgeError('AgeError: 没有年纪为 {} 的人'.format(age))
# 抛出异常
except Exception as MyError:
print(MyError)
else:
print('没有异常')
# 输入:-8
# 输出:AgeError: 没有年纪为 -8 的人
assert 断言
语句格式:assert 表达式[,返回对象]。
解释:assert 后面的表达式会计算出一个布尔值,若值为 True ,则断言成立,程序继续执行;若值为 False ,则抛出 AssertionError 异常,并打印 assert 中指定的字符串消息。
try:
age = int(input())
assert age >=0, '断言失败: 没有年纪为 {} 的人'.format(age)
except AssertionError as MyError:
print(MyError)
# 输入:-8
# 输出:断言失败: 没有年纪为 -8 的人