Python 异常处理

13.1 异常的概念

异常是程序运行时发生的错误,会中断程序正常执行。

常见异常类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ZeroDivisionError - 除零错误
# 10 / 0

# NameError - 变量未定义
# print(undefined_var)

# TypeError - 类型错误
# "2" + 2

# ValueError - 值错误
# int("abc")

# IndexError - 索引越界
# [1, 2, 3][10]

# KeyError - 键不存在
# {"a": 1}["b"]

# FileNotFoundError - 文件不存在
# open("nonexistent.txt")

13.2 try-except 语句

基本语法

1
2
3
4
5
6
try:
# 可能出错的代码
result = 10 / 0
except ZeroDivisionError:
# 处理特定异常
print("不能除以零")

多个 except

1
2
3
4
5
6
7
8
9
try:
num = int(input("输入数字: "))
result = 10 / num
except ValueError:
print("请输入有效的数字")
except ZeroDivisionError:
print("不能除以零")
except Exception as e:
print(f"发生错误: {e}")

13.3 try-except-else-finally

1
2
3
4
5
6
7
8
9
10
try:
result = 10 / 2
except ZeroDivisionError:
print("除零错误")
else:
# 没有异常时执行
print(f"结果: {result}")
finally:
# 无论是否异常都执行
print("清理工作")

实际示例

1
2
3
4
5
6
7
8
9
10
11
12
13
def read_file(filename):
try:
f = open(filename, "r")
except FileNotFoundError:
print("文件不存在")
else:
content = f.read()
print(content)
f.close()
finally:
print("操作完成")

read_file("test.txt")

13.4 捕获多个异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 方式 1:多个 except
try:
pass
except ValueError:
pass
except TypeError:
pass

# 方式 2:元组(相同处理)
try:
pass
except (ValueError, TypeError):
print("值或类型错误")

# 方式 3:获取异常信息
try:
10 / 0
except ZeroDivisionError as e:
print(f"错误: {e}")
print(f"异常类型: {type(e).__name__}")

13.5 抛出异常

raise 语句

1
2
3
4
5
6
7
8
9
10
11
def set_age(age):
if age < 0:
raise ValueError("年龄不能为负数")
if age > 150:
raise ValueError("年龄不能超过 150")
print(f"年龄设置为: {age}")

try:
set_age(-5)
except ValueError as e:
print(f"错误: {e}")

重新抛出异常

1
2
3
4
5
try:
10 / 0
except ZeroDivisionError as e:
print("记录日志...")
raise # 重新抛出

13.6 自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyError(Exception):
"""自定义异常基类"""
pass

class AgeError(MyError):
"""年龄错误异常"""
def __init__(self, age, message="年龄无效"):
self.age = age
self.message = message
super().__init__(self.message)

def set_age(age):
if age < 0 or age > 150:
raise AgeError(age, f"年龄 {age} 无效")
print(f"年龄: {age}")

try:
set_age(200)
except AgeError as e:
print(f"{e.message}: {e.age}")

13.7 异常处理最佳实践

1. 捕获具体异常

1
2
3
4
5
6
7
8
9
10
11
# 不推荐
try:
pass
except:
pass

# 推荐
try:
pass
except ValueError:
pass

2. 缩小 try 范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 不推荐
try:
data = get_data()
result = process(data)
save(result)
except Exception:
pass

# 推荐
data = get_data()
try:
result = process(data)
except ValueError:
print("数据处理失败")
return

try:
save(result)
except IOError:
print("保存失败")

3. 使用上下文管理器

1
2
3
4
5
6
7
8
9
10
# 不推荐
f = open("file.txt")
try:
content = f.read()
finally:
f.close()

# 推荐
with open("file.txt") as f:
content = f.read()

13.8 断言

断言用于调试,检查条件是否为真。

1
2
3
4
5
6
7
8
9
def divide(a, b):
assert b != 0, "除数不能为零"
return a / b

print(divide(10, 2)) # 5.0
# divide(10, 0) # AssertionError

# 关闭断言(生产环境)
# python -O script.py

13.9 异常链

1
2
3
4
5
6
7
8
9
10
11
def process_data(data):
try:
result = int(data)
except ValueError as e:
raise RuntimeError("数据处理失败") from e

try:
process_data("abc")
except RuntimeError as e:
print(f"主异常: {e}")
print(f"原因: {e.__cause__}")

13.10 本章小结

  • 使用 try-except 捕获和处理异常
  • else 在无异常时执行,finally 总是执行
  • 捕获具体异常,避免裸 except
  • 使用 raise 抛出异常
  • 自定义异常继承 Exception
  • 断言用于调试检查

练习题

  1. 编写函数,安全地将字符串转换为整数,失败返回默认值
  2. 创建自定义异常 PasswordError,用于密码验证
  3. 编写程序读取文件,处理可能的文件不存在和权限错误
  4. 以下代码输出什么?
    1
    2
    3
    4
    5
    6
    7
    8
    try:
    print(10 / 2)
    except ZeroDivisionError:
    print("错误")
    else:
    print("成功")
    finally:
    print("完成")
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
Python基础教程 系列
第 13/20 篇
分享: