Python 从入门到精通
第 22 / 36 篇
从环境安装和基础语法出发,逐步学习工程实践、自动化、数据处理与 Web 开发。
-
01
Python 怎么装、怎么用 -
02
变量、数字和字符串 -
03
列表:把一组数据放在一起 -
04
元组、集合和字典 -
05
条件判断:让程序做选择 -
06
循环:重复的事交给程序 -
07
函数:把代码整理成可复用的块 -
08
模块与包:拆分你的程序 -
09
输入、输出与字符串格式化 -
10
文件读写:保存程序的数据 -
11
异常处理:程序出错时怎么办 -
12
基础阶段练习:命令行记账本 -
13
类和对象:面向对象入门 -
14
继承、组合与特殊方法 -
15
迭代器与生成器 -
16
列表推导式与生成器表达式 -
17
装饰器:给函数增加能力 -
18
上下文管理器与 with -
19
类型标注与 dataclass -
20
正则表达式:从文本中找规律 -
21
日期、时间与时区 -
22
日志与调试正在阅读 -
23
虚拟环境与依赖管理 -
24
测试:让修改不再提心吊胆 -
25
网络请求:用 Python 调用 API -
26
网页解析与合规采集 -
27
操作 Excel、CSV 与批量文件 -
28
SQLite:给程序加一个数据库 -
29
数据分析入门:NumPy 与 Pandas -
30
画图:把数据变得直观 -
31
Flask 入门:做一个小网站 -
32
异步编程:同时处理多项任务 -
33
线程、进程与并发选择 -
34
性能分析与优化 -
35
项目结构、配置与发布 -
36
综合项目:从需求到上线
程序出错时,最怕的不是报错,而是不知道出了什么错。这一篇讲两件事:用 logging 模块规范地记录日志,以及一些实用的调试思路。把这两样练熟,排查问题的速度能快一倍。
print 调试的局限
新手调试全靠 print,但项目一大它就顶不住了:
- print 输出混在正常输出里,分不清
- 上线后 print 打给谁看?没人看,还拖慢性能
- 想关掉调试信息,得手动删一堆 print
logging 模块就是 print 的正式版:能分级、能写文件、能随时开关。
快速上手:basicConfig
import logging
logging.basicConfig(level=logging.INFO) # 设置最低级别
logging.debug("这是调试信息") # 默认不显示(级别低于 INFO)
logging.info("程序启动了")
logging.warning("磁盘空间不足 10%")
logging.error("连接数据库失败")
logging.critical("系统无法继续运行")
输出(默认格式:级别:日志器名:消息):
INFO:root:程序启动了
WARNING:root:磁盘空间不足 10%
ERROR:root:连接数据库失败
CRITICAL:root:系统无法继续运行
五个级别从低到高:DEBUG < INFO < WARNING < ERROR < CRITICAL。level= 设了门槛,低于它的不输出。开发时设 DEBUG,上线设 WARNING,一行改全局。
记录到文件 + 带时间
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
filename="app.log", # 写文件(不写则输出到控制台)
encoding="utf-8",
)
logging.info("用户登录成功")
logging.error("数据库超时")
app.log 里:
2026-08-14 16:30:00 [INFO] root: 用户登录成功
2026-08-14 16:30:01 [ERROR] root: 数据库超时
格式符:%(asctime)s 时间、%(levelname)s 级别、%(name)s 日志器名、%(message)s 消息。加了 filename 就只写文件不打印控制台,两个都要看下面”双输出”。
用模块名做日志器
大型项目里每个模块建自己的 logger,日志里能看到是哪个模块发的:
# 每个 .py 文件顶部都这样写
import logging
logger = logging.getLogger(__name__) # __name__ 就是模块名
def process():
logger.info("开始处理")
try:
...
except ValueError as e:
logger.error(f"处理失败:{e}", exc_info=True) # 带堆栈,排查时才能定位
日志会显示 main、utils.file_ops 这样的来源,多模块项目排查问题能精准定位。
控制台 + 文件双输出
想要”控制台看实时,文件留档案”,配置两个 handler:
import logging
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)
# 控制台 handler
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# 文件 handler
file_h = logging.FileHandler("app.log", encoding="utf-8")
file_h.setLevel(logging.DEBUG)
fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
console.setFormatter(fmt)
file_h.setFormatter(fmt)
logger.addHandler(console)
logger.addHandler(file_h)
logger.debug("文件里会有,控制台没有(级别不同)")
logger.info("两边都有")
理解即可,真正要配的时候照着抄。
异常要记完整堆栈
记日志时带上异常信息,用 exc_info=True 或直接 logger.exception:
import logging
logger = logging.getLogger("app")
try:
1 / 0
except ZeroDivisionError:
logger.error("计算出错", exc_info=True) # 记完整堆栈
# 或 logger.exception("计算出错") # 只能在 except 块里用,自动带堆栈
只有错误消息没有堆栈,等于没记——你根本不知道错在哪一行。日志里永远带堆栈。
调试思路:三板斧
除了日志,还有几个实用调试手段:
# 1. 断言:验证"应该成立"的条件
def withdraw(balance, amount):
assert amount > 0, "取款金额必须为正"
if amount > balance:
raise ValueError("余额不足")
return balance - amount
# 2. 手动断点:pdb(交互式调试)
def buggy(x):
import pdb
pdb.set_trace() # 程序跑到这里暂停,进入调试器
return x * 2
# 在调试器里可以:n 下一步、p 变量名 打印、q 退出
# 3. 二分定位法
# 在疑似出错的代码段前后各加一行日志,
# 看哪一半还在执行,逐步缩小范围——比瞎猜快得多
调试的通用套路:先复现 → 加日志缩小范围 → 定位到具体行 → 想清楚为什么 → 修复 → 加测试防止再犯。
新手坑
坑 1:basicConfig 只生效一次。重复调用不覆盖,第二次的配置被忽略。配置放程序入口,别放模块里。
坑 2:日志打敏感信息。密码、token、手机号别进日志——日志可能被任何人翻看,这是安全事故。
坑 3:except 里不记日志。except: pass 加日志是底线,至少 logger.exception(...)。
小结
- logging 五级别:DEBUG < INFO < WARNING < ERROR < CRITICAL,basicConfig 设门槛
- 格式:
%(asctime)s %(levelname)s %(name)s %(message)s,可写文件 - 每模块
logger = logging.getLogger(__name__),日志带来源 - 异常必记堆栈:
exc_info=True或logger.exception - 调试三板斧:assert、pdb 断点、日志二分定位
练习
- 写一个带日志的简单程序:记录”开始”、”处理成功/失败”,失败时带堆栈
- 把之前记账本的 add_record 加上日志:记录每次记账的类别和金额(不记备注,防敏感)
- 思考:生产环境日志级别应该设什么?如果设成 DEBUG 会有什么问题(提示:量、性能、敏感信息)?