首页 > 编程笔记
Python自定义异常(附带示例)
程序在运行的过程中可能会因为权限问题、用户输入问题、第三方 API 问题、请求参数缺失等问题产生异常,我们可以在程序内部通过捕获异常的语句让程序继续执行,也可以选择通过 raise 语句抛出异常让程序终止运行,如果不进行异常处理,异常最终会被 Python 解释器捕获并终止运行程序。
一般情况下,程序无法处理正常的逻辑执行过程时会发生异常。为了处理程序在运行过程中的异常和错误,Python 定义了很多的标准异常和异常处理机制来处理程序运行过程中出现的异常。
在高级编程语言中,一般都有错误和异常的概念,异常是可以捕获并被处理的,但是错误是不能被捕获的。一个健壮的程序,应尽可能地避免错误,捕获和处理各种异常。
Python 有两种错误很容易辨认:语法错误和异常。
Python 的语法错误(或称解析错误)是解析代码时出现的错误。当代码不符合 Python 语法规则时,Python 解释器在解析时就会报出 SyntaxError 语法错误,同时还会明确指出探测到错误的语句和报错原因,如少了冒号、混用中英文符号等。
即便 Python 程序的语法是正确的,在程序运行时,也有可能发生错误,运行期间检测到的错误被称为异常。大多数的异常都不会被程序处理,都以错误信息的形式展现,如除数为 0、年龄为负数、数组下标越界等。
异常处理在任何一门编程语言里都是值得关注的话题,良好的异常处理可以让程序更加健壮,清晰的错误信息能帮助开发人员快速修复问题。
开发人员可以使用异常处理全面地控制自己的程序。异常处理大大提高了程序的健壮性和人机交互的友好性。
在 Python 语言中,处理异常的关键字主要有 try、except、else、finally 和 raise:
处理异常的关键字通常组合使用,不同的组合能实现不同的异常处理场景。合理的异常处理不仅能完善程序运行过程中的逻辑,也能提升程序运行的性能。语法如下:
一个 try 语句可以对应多个 expect 子句,但只能对应一个 finally 子句。我们可以使用 try-expect 组合检测和处理异常,也可以添加一个可选的 else 子句处理没有检测到异常的执行代码。而 finally 子句只用于检测异常和做一些必要的清除工作,例如无论是否发生异常,都要关闭连接。
Python 用异常对象来表示异常,当遇到错误,会触发异常。如果异常对象未被捕捉或处理,程序就会被终止执行。
如前文所述,Python 中自定义异常类型非常简单,只需要从 Exception 类继承即可。通过创建一个新的异常类,我们可以命名自己的异常。
在如下示例中我们创建了一个自定义异常类 Networkerror,基类为 BaseException,用于在异常触发时输出更多的信息:
如果我们需要自主抛出一个异常,可以使用 raise 关键字抛出异常,如上述示例中的 raise Networkerror('类型错误')。
raise 的通常语法为:
在实际调试程序的过程中,有时只获得异常的类型是远远不够的,还需要借助更详细的异常信息才能解决问题。捕获异常时,有两种方式可获得更多的异常信息,分别是:
解决方法为将 Tab 转换成 4 个 Space。
我们自定义异常类 APIException,返回的信息包括内部错误码、错误信息和请求的 URL 如代码清单1所示。
代码清单1:APIException
代码清单2:APIExceptionExample
注意,虽然此处表述为异常类,但是我们可以在类中定义响应成功的返回,例如代码清单2中定义的状态码 201 对应的异常类,可以作为响应成功的返回。
一般情况下,程序无法处理正常的逻辑执行过程时会发生异常。为了处理程序在运行过程中的异常和错误,Python 定义了很多的标准异常和异常处理机制来处理程序运行过程中出现的异常。
在高级编程语言中,一般都有错误和异常的概念,异常是可以捕获并被处理的,但是错误是不能被捕获的。一个健壮的程序,应尽可能地避免错误,捕获和处理各种异常。
Python 有两种错误很容易辨认:语法错误和异常。
Python 的语法错误(或称解析错误)是解析代码时出现的错误。当代码不符合 Python 语法规则时,Python 解释器在解析时就会报出 SyntaxError 语法错误,同时还会明确指出探测到错误的语句和报错原因,如少了冒号、混用中英文符号等。
即便 Python 程序的语法是正确的,在程序运行时,也有可能发生错误,运行期间检测到的错误被称为异常。大多数的异常都不会被程序处理,都以错误信息的形式展现,如除数为 0、年龄为负数、数组下标越界等。
异常处理在任何一门编程语言里都是值得关注的话题,良好的异常处理可以让程序更加健壮,清晰的错误信息能帮助开发人员快速修复问题。
Python 异常处理方法
默认情况下,程序发生异常是会终止的,如果我们要避免程序终止执行,可以使用捕获异常的方式获取这个异常的名称,再通过其他的逻辑代码让程序继续运行,这种处理叫作异常处理。开发人员可以使用异常处理全面地控制自己的程序。异常处理大大提高了程序的健壮性和人机交互的友好性。
在 Python 语言中,处理异常的关键字主要有 try、except、else、finally 和 raise:
- try 关键字:用于检测异常,在程序发生异常时将异常信息交给 except 关键字。
- except 关键字:获取异常并进行处理。
- else 关键字:当执行完 try 关键字域中的代码,如果没有发现异常,则接着执行 else 关键字域中的代码。
- finally 关键字:无论是否发生异常都进入该关键字域进行处理,通常用于处理资源关闭、对象内存释放等必需的操作。
- raise 关键字:用于抛出自定义的异常信息使程序不能直接向下执行。
处理异常的关键字通常组合使用,不同的组合能实现不同的异常处理场景。合理的异常处理不仅能完善程序运行过程中的逻辑,也能提升程序运行的性能。语法如下:
try: <语句> # 待捕获异常的代码 except <异常类>: <语句> # 捕获某种类型的异常 except <异常类> as <变量名>: <语句> # 捕获某种类型的异常并获得对象 else: <语句> # 如果没有异常发生,则执行 finally: <语句> # 退出try时总会执行,不管是否发生了异常,都要执行finally的部分上述语法的具体含义如下:
- try 中语句在执行时发生异常,搜索 except 子句,并执行第一个匹配该异常的 except 子句。
- try 中语句在执行时发生异常,却没有匹配的 except 子句,异常将被递交到外层的 try,如果外层不处理该异常,异常将继续向外层传递。如果都不处理该异常,则会传递到最外层,如果还没有处理,就终止异常所在的线程。
- try 中语句在执行时没有发生异常,如果有 else 子句,可执行 else 子句中的语句。
- 无论 try 中语句在执行时是否发生异常,finally 子句中的语句都会执行。
一个 try 语句可以对应多个 expect 子句,但只能对应一个 finally 子句。我们可以使用 try-expect 组合检测和处理异常,也可以添加一个可选的 else 子句处理没有检测到异常的执行代码。而 finally 子句只用于检测异常和做一些必要的清除工作,例如无论是否发生异常,都要关闭连接。
Python 自定义异常
用户自定义异常,只要自定义异常类继承了 Exception 类即可。在自定义异常类时,我们基本不需要书写很多的代码,表1所示是 Python 中所有标准异常类。Python 用异常对象来表示异常,当遇到错误,会触发异常。如果异常对象未被捕捉或处理,程序就会被终止执行。
常见异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行,捕获用户中断行为,通常为按Ctrl + C键 |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
SystemExit | Python解释器请求退出 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零(所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
IOError | 输入/输出操作失败 |
ImportError | 导入失败 |
IndexError | 序列中没有这个索引 |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误 |
NameError | 未声明/初始化对象(没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python语法错误 |
IndentationError | 缩进错误 |
TabError | Tab和空格混用 |
ValueError | 传入无效的参数 |
如前文所述,Python 中自定义异常类型非常简单,只需要从 Exception 类继承即可。通过创建一个新的异常类,我们可以命名自己的异常。
在如下示例中我们创建了一个自定义异常类 Networkerror,基类为 BaseException,用于在异常触发时输出更多的信息:
class Networkerror(BaseException): def __init__(self,msg): self.msg=msg def __str__(self): return self.msg try: raise Networkerror('类型错误') except Networkerror as e: print(e)在 try 中,若发生自定义异常,则执行 except 子句,其中变量 e 是用于创建 Networkerror 类的实例。
如果我们需要自主抛出一个异常,可以使用 raise 关键字抛出异常,如上述示例中的 raise Networkerror('类型错误')。
raise 的通常语法为:
raise异常类名称(描述信息)
在触发指定类型的异常的同时,附带异常的描述信息。在实际调试程序的过程中,有时只获得异常的类型是远远不够的,还需要借助更详细的异常信息才能解决问题。捕获异常时,有两种方式可获得更多的异常信息,分别是:
- 使用 sys 库中的 exc_info 方法;
- 使用 traceback 库中的相关函数。
TabError 的解决方法
Python 文件运行时报错:TabError: inconsistent use of tabs and spaces in indentation
究其原因是 Python 文件中混用 Tab 和 Space 实现格式缩进,通常使用外部编辑器编辑 Python 文件时,会自动采用 Tab 进行格式缩进。解决方法为将 Tab 转换成 4 个 Space。
示例
在进行 Web 开发的工程中,如果采用 Flask 框架,那么需要自定义异常处理类,方便统一进行异常的处理。此时自定义的异常处理类应该继承自 HTTPException,自定义的内容通常包含如下几点:- 定义想要返回的错误信息的 JSON 格式,如内部错误码、错误信息等;
- 更改返回的响应头,返回 JSON 格式的信息响应头应设为 'Content-Type': 'application/json';
- 与 HTTPException 一样,定义状态码。
我们自定义异常类 APIException,返回的信息包括内部错误码、错误信息和请求的 URL 如代码清单1所示。
代码清单1:APIException
# -*- coding: utf-8 -*- # @Time : 2022/7/11 4:21 下午 # @Project : ExceptionTestDemo # @File : APIException.py # @Version: Python3.9.8 from flask import request, json from werkzeug.exceptions import HTTPException class APIException(HTTPException): code = 500 msg = 'sorry, we made a mistake!' error_code = 999 def __init__(self, msg=None, code=None, error_code=None, headers=None): if code: self.code = code if error_code: self.error_code = error_code if msg: self.msg = msg super(APIException, self).__init__(msg, None) def get_body(self, environ=None): body = dict( msg=self.msg, error_code=self.error_code, request=request.method + ' ' + self.get_url_no_param() ) text = json.dumps(body) return text def get_headers(self, environ=None): """Get a list of headers.""" return [('Content-Type', 'application/json')] @staticmethod def get_url_no_param(): full_path = str(request.full_path) main_path = full_path.split('?') return main_path[0]有了 APIException 类,我们就可以自由地定义各种状态码以及对应的异常信息,然后在合适的位置抛出异常,如代码清单2所示。
代码清单2:APIExceptionExample
# -*- coding: utf-8 -*- # @Time : 2022/7/11 5:21 下午 # @Project : ExceptionTestDemo # @File : APIExceptionExample.py # @Version: Python3.9.8 import APIException class Success(APIException): code = 201 msg = 'ok' error_code = 0 class ServerError(APIException): code = 500 msg = 'sorry, we made a mistake!' error_code = 999 class ParameterException(APIException): code = 400 msg = 'invalid parameter' error_code = 1000 class NotFound(APIException): code = 404 msg = 'the resource are not found' error_code = 1001自定义异常类可以帮助我们在需要的地方抛出异常。在发生异常时,我们可以对照状态码去查找对应的异常类,非常方便。
注意,虽然此处表述为异常类,但是我们可以在类中定义响应成功的返回,例如代码清单2中定义的状态码 201 对应的异常类,可以作为响应成功的返回。