第3章:调试
一、导读
1.1 本章学习目标
第3章讲解 Python 的调试技巧:logging、异常处理、调试器、测试。
本章核心学习目标:
- 掌握 logging:比 print 更好的调试方式
- 理解异常处理:try/except/else/finally
- 掌握 pdb 调试器
- 了解单元测试
1.2 为什么本章如此重要
调试是编程中最重要的技能之一。好的调试习惯能大幅减少 bug 的排查时间。
1.3 核心问题引导
logging 比 print 有什么优势?
什么时候应该捕获异常,什么时候应该让它传播?
pdb 的常用命令有哪些?
二、核心概念详解
2.1 Logging
python
import logging
# 基本配置
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")
logger.critical("Critical message")
# 不同模块使用不同的 logger
# module1.py
logger1 = logging.getLogger('module1')
# module2.py
logger2 = logging.getLogger('module2')2.2 异常处理
python
# 基本异常处理
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}")
except (TypeError, ValueError) as e:
print(f"Type/Value error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
else:
print("No error occurred") # 没有异常时执行
finally:
print("Always executed") # 总是执行
# 自定义异常
class AppError(Exception):
pass
class ValidationError(AppError):
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
# 使用
try:
raise ValidationError("email", "Invalid format")
except ValidationError as e:
print(f"Validation failed for {e.field}: {e.message}")2.3 调试器
python
import pdb
def buggy_function(data):
total = 0
for item in data:
# pdb.set_trace() # 设置断点
total += item['value']
return total
# 常用 pdb 命令:
# n (next): 执行下一行
# s (step): 进入函数
# c (continue): 继续执行
# p variable: 打印变量
# l (list): 显示代码
# q (quit): 退出调试
# Python 3.7+ 可以用 breakpoint()
def modern_debug(data):
total = 0
for item in data:
breakpoint() # 等同于 pdb.set_trace()
total += item['value']
return total三、实战案例
3.1 案例一:带日志的文件处理
python
import logging
from pathlib import Path
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def process_files(directory):
dir_path = Path(directory)
if not dir_path.exists():
logger.error(f"Directory not found: {directory}")
return
processed = 0
for file_path in dir_path.glob('*.txt'):
try:
content = file_path.read_text()
# 处理文件
result = content.upper()
file_path.write_text(result)
processed += 1
logger.info(f"Processed: {file_path.name}")
except PermissionError:
logger.warning(f"Permission denied: {file_path}")
except Exception as e:
logger.error(f"Error processing {file_path}: {e}")
logger.info(f"Total processed: {processed}")四、本章小结
logging 比 print 更灵活,支持级别、格式、输出目标
异常处理应该精确捕获,避免裸 except
pdb/breakpoint() 是强大的调试工具
五、思考与练习
logging 的级别有哪些?各适合什么场景?
如何实现日志轮转(防止日志文件过大)?