个人主页:Guiat
归属专栏:Python
文章目录
- 1. 错误和异常的概念
- 1.1 错误
- 1.2 异常
- 2. 常见的内置异常类型
- 2.1 `ZeroDivisionError`
- 2.2 `IndexError`
- 2.3 `KeyError`
- 2.4 `TypeError`
- 3. 异常处理机制
- 3.1 `try-except` 语句
- 3.2 `try-except-else` 语句
- 3.3 `try-except-finally` 语句
- 4. 异常的抛出
- 4.1 `raise` 语句
- 4.2 异常链
- 5. 自定义异常
- 5.1 定义自定义异常类
- 5.2 使用自定义异常
- 6. 异常的捕获顺序与多异常捕获
- 6.1 异常捕获顺序
- 6.2 多异常捕获
- 7. 异常信息的获取
- 7.1 获取异常对象
- 7.2 异常对象的属性和方法
- 8. 异常处理的最佳实践
- 8.1 精确捕获异常
- 8.2 异常处理的粒度
- 8.3 资源管理与异常处理
- 9. 异常在模块和类中的应用
- 9.1 模块中的异常处理
- 9.2 类中的异常处理
正文
1. 错误和异常的概念
1.1 错误
在 Python 里,错误一般可分为语法错误和逻辑错误。
- 语法错误:是指代码编写不符合 Python 语法规则,导致 Python 解释器无法正确解析代码。例如,少了冒号、括号不匹配等。
# 语法错误示例,if 语句后缺少冒号
if Trueprint("This is a syntax error.")
- 逻辑错误:代码语法上没有问题,但程序运行结果不符合预期。这通常是由于算法设计错误、变量使用不当等原因造成的。例如,在计算平均值时,分母可能为 0。
1.2 异常
异常是在程序运行过程中出现的错误事件,它会打断程序的正常执行流程。Python 有许多内置异常类型,如 ZeroDivisionError
、IndexError
等。
2. 常见的内置异常类型
2.1 ZeroDivisionError
当试图用一个数除以 0 时,会引发该异常。
try:result = 5 / 0
except ZeroDivisionError:print("不能除以零!")
2.2 IndexError
当使用的索引超出序列(如列表、元组)的有效范围时,会触发此异常。
my_list = [1, 2, 3]
try:print(my_list[3])
except IndexError:print("索引超出范围!")
2.3 KeyError
在字典中使用不存在的键进行访问时,会抛出 KeyError
。
my_dict = {'a': 1, 'b': 2}
try:print(my_dict['c'])
except KeyError:print("键不存在!")
2.4 TypeError
当操作或函数应用于不适当类型的对象时,会引发该异常。例如,将字符串和整数相加。
try:result = "hello" + 1
except TypeError:print("类型不匹配,不能将字符串和整数相加!")
3. 异常处理机制
3.1 try-except
语句
try-except
语句用于捕获和处理异常。try
块中放置可能会引发异常的代码,except
块用于处理捕获到的异常。
try:num1 = int(input("请输入一个整数: "))num2 = int(input("请输入另一个整数: "))result = num1 / num2print(f"结果是: {result}")
except ValueError:print("输入不是有效的整数!")
except ZeroDivisionError:print("不能除以零!")
3.2 try-except-else
语句
else
块在 try
块中的代码没有引发异常时执行。
try:num = int(input("请输入一个整数: "))
except ValueError:print("输入不是有效的整数!")
else:print(f"你输入的整数是: {num}")
3.3 try-except-finally
语句
finally
块无论 try
块中是否发生异常,都会被执行。常用于释放资源等操作。
file = None
try:file = open("test.txt", "r")content = file.read()print(content)
except FileNotFoundError:print("文件未找到!")
finally:if file:file.close()
4. 异常的抛出
4.1 raise
语句
可以使用 raise
语句手动抛出异常。例如,自定义一个异常类,并在满足特定条件时抛出该异常。
class CustomError(Exception):passdef check_number(num):if num < 0:raise CustomError("输入的数字不能为负数!")return numtry:result = check_number(-5)
except CustomError as e:print(e)
4.2 异常链
在抛出一个异常时,可以使用 raise ... from ...
语句将当前异常与另一个异常关联起来,形成异常链。
try:num = int("abc")
except ValueError as original_exception:raise CustomError("输入无法转换为整数!") from original_exception
5. 自定义异常
5.1 定义自定义异常类
通过继承内置的 Exception
类或其子类,可以创建自定义异常类。
class MyCustomError(Exception):def __init__(self, message):self.message = messagesuper().__init__(self.message)
5.2 使用自定义异常
在代码中可以像使用内置异常一样使用自定义异常。
def validate_age(age):if age < 0:raise MyCustomError("年龄不能为负数!")return agetry:valid_age = validate_age(-3)
except MyCustomError as e:print(e.message)
6. 异常的捕获顺序与多异常捕获
6.1 异常捕获顺序
在使用多个 except
子句时,Python 会按照 except
子句的顺序依次检查捕获的异常类型。一旦匹配到相应的异常类型,就会执行该 except
子句中的代码,并且不会再继续检查后续的 except
子句。因此,捕获范围小的异常应该放在前面,捕获范围大的异常放在后面。
try:num = int("abc")result = 1 / num
except ValueError:print("输入无法转换为整数!")
except ZeroDivisionError:print("不能除以零!")
except Exception:print("发生了其他未知异常!")
在上述代码中,ValueError
捕获范围相对较小,先进行检查。如果输入无法转换为整数,就会执行对应的 except
子句。如果没有匹配到 ValueError
,再检查 ZeroDivisionError
等其他异常。
6.2 多异常捕获
可以在一个 except
子句中捕获多种不同类型的异常,使用元组将这些异常类型括起来。
try:num = int("abc")result = 1 / num
except (ValueError, ZeroDivisionError):print("输入错误或者进行了除以零的操作!")
这种方式适用于不同异常类型需要执行相同处理逻辑的情况。
7. 异常信息的获取
7.1 获取异常对象
在 except
子句中,可以通过给异常类型后面指定一个变量名来获取异常对象,从而访问异常的相关信息,如异常的错误消息。
try:num = int("abc")
except ValueError as ve:print(f"捕获到 ValueError 异常,错误信息: {ve}")
在上述代码中,ve
就是捕获到的 ValueError
异常对象,通过它可以获取具体的错误消息。
7.2 异常对象的属性和方法
不同的异常类型可能有不同的属性和方法。例如,OSError
异常通常会包含有关操作系统错误的详细信息,如错误号和错误消息。
import ostry:file = open("nonexistent_file.txt", "r")
except OSError as oe:print(f"错误号: {oe.errno}")print(f"错误消息: {oe.strerror}")
这里通过 oe.errno
获取错误号,通过 oe.strerror
获取具体的错误消息。
8. 异常处理的最佳实践
8.1 精确捕获异常
尽量精确地捕获特定类型的异常,而不是使用通用的 except
子句捕获所有异常。这样可以更清晰地处理不同类型的错误,避免隐藏潜在的问题。
try:num = int("abc")
except ValueError:print("输入无法转换为整数!")
而不是:
try:num = int("abc")
except:print("发生了异常!")
8.2 异常处理的粒度
异常处理的粒度要适中。如果粒度太粗,可能会掩盖一些重要的错误信息;如果粒度太细,会使代码变得复杂。例如,在一个函数中,对于不同的操作可以分别进行异常处理。
def process_data():try:# 读取数据的操作data = read_data()except FileNotFoundError:print("数据文件未找到!")returntry:# 处理数据的操作result = analyze_data(data)except ValueError:print("数据处理过程中发生值错误!")returnreturn result
8.3 资源管理与异常处理
在处理需要手动释放资源(如文件、网络连接等)的情况时,要确保资源在异常发生时也能被正确释放。可以使用 try - finally
语句或 with
语句。with
语句是一种更简洁、更安全的资源管理方式,它会自动处理资源的获取和释放。
with open("test.txt", "r") as file:content = file.read()print(content)
# 无需手动调用 file.close(),with 语句会自动处理
9. 异常在模块和类中的应用
9.1 模块中的异常处理
在模块中,通常会定义一些函数供其他模块调用。在这些函数中,要合理处理可能出现的异常,并根据情况将异常抛出给调用者。
# module.py
def divide_numbers(a, b):if b == 0:raise ZeroDivisionError("除数不能为零!")return a / b
# main.py
import moduletry:result = module.divide_numbers(5, 0)
except ZeroDivisionError as zde:print(zde)
9.2 类中的异常处理
在类的方法中,也需要进行异常处理。可以根据类的功能和使用场景,自定义异常类并在合适的时候抛出。
class BankAccount:def __init__(self, balance):if balance < 0:raise ValueError("账户余额不能为负数!")self.balance = balancedef withdraw(self, amount):if amount > self.balance:raise ValueError("取款金额超过账户余额!")self.balance -= amountreturn self.balancetry:account = BankAccount(-10)
except ValueError as ve:print(ve)try:valid_account = BankAccount(100)valid_account.withdraw(200)
except ValueError as ve:print(ve)
在上述代码中,BankAccount
类的初始化方法和 withdraw
方法都进行了异常处理,确保对象的状态和操作的合法性。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!