1. 什么是错误和异常
在Python中,错误可以分为两类:
- 语法错误(Syntax Errors):代码不符合Python语法规则
- 异常(Exceptions):语法正确的代码在运行时发生的错误
python
# 语法错误示例
print("Hello World" # 缺少右括号
# 异常示例
print(10 / 0) # ZeroDivisionError: division by zero
2. 常见的异常类型
Python内置了许多异常类型,常见的有:
python
# NameError - 尝试访问未定义的变量
print(undefined_variable)
# TypeError - 类型操作错误
"2" + 2 # 不能将字符串和整数相加
# IndexError - 索引超出范围
lst = [1, 2, 3]
print(lst[3](@ref)
# KeyError - 字典键不存在
d = {'a': 1}
print(d['b'])
# FileNotFoundError - 文件不存在
open('nonexistent.txt')
3. 异常处理:try-except语句
使用try-except可以捕获并处理异常:
python
try:
# 可能引发异常的代码
result = 10 / 0
except ZeroDivisionError:
# 处理特定异常
print("不能除以零!")
捕获多种异常
python
try:
num = int(input("请输入一个数字: "))
result = 100 / num
print("结果是:", result)
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("不能输入零!")
except Exception as e: # 捕获所有其他异常
print(f"发生未知错误: {e}")
4. try-except-else-finally完整结构
python
try:
file = open('example.txt', 'r')
content = file.read()
except FileNotFoundError:
print("文件不存在!")
else:
# 如果没有异常发生,执行else块
print("文件内容:", content)
finally:
# 无论是否发生异常都会执行
print("清理工作...")
if 'file' in locals() and not file.closed:
file.close()
5. 抛出异常:raise语句
我们可以主动抛出异常:
python
def check_age(age):
if age < 0:
raise ValueError("年龄不能为负数")
elif age < 18:
raise ValueError("未成年禁止访问")
else:
print("欢迎访问")
try:
check_age(-5)
except ValueError as e:
print(f"错误: {e}")
6. 自定义异常
我们可以创建自己的异常类型:
python
class MyCustomError(Exception):
"""自定义异常类 - 用于演示特定业务场景的错误"""
def __init__(self, message, error_code=500):
self.message = message
self.error_code = error_code
super().__init__(f"错误代码 {error_code}: {message}")
def __str__(self):
"""自定义异常信息的字符串表示形式"""
return f"[{self.__class__.__name__}] {self.message}"
def calculate_division(dividend, divisor):
"""演示函数:执行除法运算,当条件不满足时抛出不同类型的异常"""
if not isinstance(dividend, (int, float)) or not isinstance(divisor, (int, float)):
raise TypeError("被除数和除数必须是数字类型")
if divisor == 0:
raise ZeroDivisionError("除数不能为零")
if dividend < 0 or divisor < 0:
# 抛出自定义异常:当输入为负数时
raise MyCustomError("计算不支持负数输入", error_code=400)
return dividend / divisor
def main():
"""主函数:演示异常的捕获和处理流程"""
print("=== 自定义异常演示程序 ===")
while True:
try:
# 获取用户输入
num1 = float(input("\n请输入被除数(输入q退出):"))
num2 = float(input("请输入除数:"))
# 执行计算
result = calculate_division(num1, num2)
print(f"计算结果:{num1} ÷ {num2} = {result}")
except ValueError as e:
# 处理非数字输入
if "q" in str(e).lower():
print("程序已退出")
break
print(f"输入错误:请输入有效的数字。错误详情:{e}")
except ZeroDivisionError as e:
# 处理内置异常
print(f"数学错误:{e}")
except MyCustomError as e:
# 处理自定义异常
print(f"业务错误:{e}(错误代码:{e.error_code})")
except TypeError as e:
# 处理类型错误
print(f"类型错误:{e}")
except Exception as e:
# 捕获所有其他未预期的异常
print(f"未知错误:{e}")
break
if __name__ == "__main__":
main()
这个示例程序包含以下关键特性:
- 自定义异常类 :
MyCustomError
继承自Exception
,包含错误信息和错误代码- 异常抛出函数 :
calculate_division
根据不同条件抛出不同类型的异常- 多级异常捕获 :在
main
函数中使用多个except
块分别处理不同类型的异常- 用户交互:通过控制台输入演示异常的触发和处理流程
你可以运行这段代码并尝试以下输入场景:
- 正常输入两个正数(如
10
和2
)- 输入零作为除数(触发
ZeroDivisionError
)- 输入负数(触发
MyCustomError
)- 输入非数字字符(触发
ValueError
)- 输入
q
退出程序
通过观察不同输入下的输出结果,你可以深入理解自定义异常的工作机制和优势。
7. 断言assert
assert用于确保某个条件为真,否则抛出AssertionError:
python
def divide(a, b):
assert b != 0, "除数不能为零"
return a / b
print(divide(10, 2)) # 正常
print(divide(10, 0)) # 抛出AssertionError
8. 实际应用案例
案例1:处理用户输入
python
while True:
try:
age = int(input("请输入您的年龄: "))
if age < 0:
raise ValueError("年龄不能为负数")
break
except ValueError as e:
print(f"无效输入: {e}")
print(f"您的年龄是: {age}")
案例2:文件操作
python
def read_file(filename):
try:
with open(filename, 'r') as file:
return file.read()
except FileNotFoundError:
print(f"文件 {filename} 不存在")
return None
except IOError:
print(f"读取文件 {filename} 时发生错误")
return None
content = read_file('data.txt')
if content:
print("文件内容:", content)
案例3:网络请求
python
import requests
def fetch_url(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # 如果请求不成功,抛出HTTPError
return response.text
except requests.exceptions.Timeout:
print("请求超时")
except requests.exceptions.HTTPError as err:
print(f"HTTP错误: {err}")
except requests.exceptions.RequestException as err:
print(f"请求错误: {err}")
return None
html = fetch_url("https://www.example.com")
if html:
print("获取内容成功!")
9. 异常处理的最佳实践
- 1.不要过度使用try-except:只捕获你知道如何处理的异常
- 2.尽量具体:捕获特定异常而不是通用的Exception
- 3.记录异常信息:使用logging模块记录异常详情
- 4.保持简洁:try块中只包含可能引发异常的代码
- 5.清理资源:使用finally或上下文管理器(with语句)确保资源释放
python
import logging
logging.basicConfig(filename='app.log', level=logging.ERROR)
def process_data(data):
try:
# 只包含可能引发异常的代码
result = complex_operation(data)
except ValueError as e:
logging.error(f"处理数据时发生值错误: {e}")
return None
except DatabaseError as e:
logging.error(f"数据库错误: {e}")
return None
else:
return result
10. 总结
异常处理是Python编程中非常重要的部分,合理使用异常处理可以使程序:
- 更加健壮,能够处理意外情况
- 更易于调试和维护
- 提供更好的用户体验
异常处理不是用来隐藏错误的,而是为了优雅地处理错误情况!