异常(异常是python对象) 和 自定义异常类

Python提供了异常断言来处理程序在运行过程中出现的异常和错误

什么是异常?

分清楚程序发生异常和程序执行错误,它们完全是两码事,
程序由于错误导致的运行异常,是需要程序员想办法解决的;
但还有一些异常,是程序正常运行的结果,比如用 raise 手动引发的异常。
异常是在程序执行过程中发生的影响程序正常执行的一个事件。
当Python检测到一个错误时,解释器就会指出当前流已无法继续执行下去,这时候就出现了异常。
**异常是指因为程序出错而在正常控制流以外采取的行为。 **
(原来异常的设计是一个有用的东西,有了异常之后程序出错就会终止,而不会出现正常控制流以外采取的行动)

异常是Python对象,当Python无法正常处理程序时就会抛出一个异常。
(看柳青老师ppt中有 BaseException是所有异常的基类,而Exception是常规错误的基类,所以python中的很多异常都是BaseException的派生类)

一旦Python脚本发生异常,程序需要捕获并处理它,否则程序会终止执行。
异常处理使程序能够处理完异常后继续它的正常执行,不至于使程序因异常导致退出或崩溃。
语法错误和逻辑错误不属于异常,但有些语法错误往往会导致异常,例如由于大小写拼写错误而访问不存在的对象。

异常分为两个阶段:

  • 第一个阶段是引起异常发生的错误;
  • 第二个阶段是检测并处理阶段。
    当程序出现错误,python会自动引发异常,也可以通过raise显式地引发异常。

python中的标准异常

Python异常体系结构如下图:

image

常见的异常如下表所示:

异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueErrorv 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

注意,BaseException是异常的顶级类。但是这个类不能当作是由用户定义的类直接继承的,而是要继承Exception。Exception类是与应用相关的异常的顶层根超类,除了系统退出事件类之外(SystemExit、KeyboardInterrupt和GeneratorExit),几乎所有的用户定义的类都应该继承自这个类,而不是BaseException

捕获与处理异常

try:
	<可能出现异常的语句块> 
except <异常类名字name1>:
	<异常处理语句块1>   
	#如果在try部份引发了'name1'异常,执行这部分语句
except <异常类名字name2>,<数据>:
	<异常处理语句块2>   
	#如果引发了'name2'异常,获得附加的数据except:
	<异常处理语句块n>   
	#如果引发了异常,但与上述异常都不匹配,执行此语句块
else:
	<else语句块>
	#如果没有上述所列的异常发生,执行else语句块
finally:
	<始终执行的语句块>

try/except语句用来检测try语句块中的异常,让except语句捕获异常信息并处理。
如果不想在异常发生时结束程序,只需要在try里捕获它,并在except中处理捕获到的异常。
try中的语句块先执行。
如果try语句块中的某一语句执行时发生异常,Python就跳到except部分,
从上到下判断抛出的异常对象是否与except后面的异常类相匹配,并执行第一个匹配该异常的except后面的语句块,异常处理完毕。
如果异常发生了,但是没有找到匹配的异常类别,则执行不带任何匹配类型的except语句后面的语句块,异常处理完毕。

示例代码

try:
    #正常执行的代码
    pass
except <错误1>:
    #抛出错误1时执行的代码
    pass
except <错误2>:
    #抛出错误2时执行的代码
    pass
except <错误3, 错误4>:
    #抛出错误3或者错误4时执行的代码
    pass
except Exception as result:
    print("未知错误类型:{}".format(result))
else:
    #没有异常才会执行的代码
    pass
finally:
    #无论是否有异常都会执行的代码
    print("程序结束")

异常的传递

当函数/方法执行出现异常会将异常传递给函数/方法的调用一方
如果传递到主程序,仍然没有异常处理程序才会被终止
注意:
在开发中,可以在主函数中增加异常捕获,这样在主函数中调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中
这样就不需要在代码中,增加大量的异常捕获能够保证代码的整洁

示例

def demo1():
    return int(input("请输入一个整数:"))


def demo2():
    return demo1()

def main():
    try:
        print(demo2())
    except ValueError:
        print("请输入正确的整数")
    except Exception as result:
        print("未知错误 %s" % result)

main()

自定义异常类

仅仅使用标准模块中的异常类通常不能满足系统开发的需要
有时候需要自定义一些异常类
系统无法识别自定义的异常类,只能在程序中显式地使用raise抛出异常
可以通过扩展BaseException类或其子类来创建自定义异常类
BaseException是所有异常的基类

raise的作用

raise 语句的基本语法格式为:raise [exceptionName [(reason)]]

其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。
也就是说,raise 语句有如下三种常用的用法:

  1. raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
  2. raise 异常类名称:raise 后带一个异常类名称,表示引发执行类型的异常。
  3. raise 异常类名称(描述信息):在引发指定类型的异常的同时,附带异常的描述信息。

示例如下

>>> raise
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    raise
RuntimeError: No active exception to reraise
>>> raise ZeroDivisionError
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    raise ZeroDivisionError
ZeroDivisionError
>>> raise ZeroDivisionError("除数不能为零")
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    raise ZeroDivisionError("除数不能为零")
ZeroDivisionError: 除数不能为零

需要注意的是,就算是直接raise也会触发异常RuntimeError

理解异常是一个类

通过一个例子来体会

>>> class ShortInputError(Exception):
...     def __init__ (self,length,atleast):
...         Exception.__init__(self)
...         self.length = length
...         self.atleast = atleast
...
>>> try:
...     s = "123456"
...     if len(s) <10:
...         raise ShortInputError(len(s),10)
... except ShortInputError as result:
...     print("长度至少是{}".format(result.atleast))
... else:
...     print("没有异常")
...
长度至少是10

从这个例子很容易看出来,其中ShortInputError是一个派生类,基类是Exception,在这个例子中,如果s的长度小于10,就会抛出ShortInputError,然后异常就会被try捕获,然后通过except匹配,如果匹配上的话,输出带有具体信息的异常

断言与上下文管理

断言与上下文管理是两种比较特殊的异常处理方式,在形式上比异常处理结构要简单一些。
断言是申明表达式为真的判定。如果表达式为假则抛出异常。
断言语句可以理解为raise-if-not语句,用来测试表示式,如果返回值为假,则触发异常。
如果断言成功,则程序不采取任何措施,否则触发AssertionError异常。
断言的语法格式如下:
assert expression [, arguments]

注意一下三条命令就可以明白assert的作用

>>> assert 1+1 ==2
>>> assert 1+1 !=2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>> assert 1+1 !=2,"错误"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: 错误

Reference

  1. Python——内建异常体系结构
  2. 3.12.2 Documentation » The Python Standard Library » Built-in Exceptions
  3. 3.12.2 Documentation » The Python Tutorial » 8. Errors and Exceptions
posted @   光辉233  阅读(218)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示