3Automate the Boring Stuff with Python(第2版)

调试

调试技巧

logging异常处理调试器测试

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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 的级别有哪些?各适合什么场景?

如何实现日志轮转(防止日志文件过大)?


3/7调试
0%