python默认不显示deprecationwarning是设计使然,为避免污染生产日志;开发时需用-w default::deprecationwarning或warnings.simplefilter("default", deprecationwarning)手动启用。

为什么warnings.warn()发出的警告默认不显示
Python 的 warnings 模块默认只在首次触发同类警告时向 stderr 输出一次,且仅当警告级别为 DeprecationWarning 时——在命令行运行脚本时还**不会显示**(因为 Python 默认将 DeprecationWarning 过滤掉了)。这不是 bug,是设计:避免污染生产环境日志。但开发/测试时你得主动“打开开关”,否则根本看不到弃用提示。
常见现象:pip install 后看到一堆 DeprecationWarning,但自己跑脚本却静悄悄;或单元测试里调用了已弃用函数,却没报错也没提示。
- 临时启用所有弃用警告:启动 Python 时加
-W default::DeprecationWarning - 代码中全局开启:调用
warnings.simplefilter("default", DeprecationWarning) - 注意:模块级过滤器(如
__future__导入)可能覆盖你的设置,优先级需留意
用warnings.catch_warnings()临时捕获并检查警告
当你需要在测试中验证某段代码是否真的发出了弃用警告(比如写单元测试),不能靠打印日志判断,得用上下文管理器捕获对象本身。关键点是必须设 record=True,否则 warnings 不存实例。
import warnings
<p>def old_func():
warnings.warn("old_func is deprecated", DeprecationWarning)
return 42</p><p>with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always") # 必须设为 always,否则 DeprecationWarning 仍被忽略
result = old_func()
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "deprecated" in str(w[0].message)
</p>
-
simplefilter("always")是核心,它让所有警告都触发并记录,绕过默认过滤策略 -
record=True让catch_warnings()返回警告对象列表,每个含.message、.category、.filename等属性 - 别漏掉
assert issubclass(...)—— 直接比字符串容易误匹配其他警告类型
如何让弃用警告变成异常(强制中断)
在 CI 或本地开发阶段,你想让任何弃用警告直接失败,而不是等上线后才发现。这时把警告转成异常最有效。但注意:不是所有场景都适合,比如你依赖的第三方库还在发弃用警告,而你暂时无法升级。
- 全局转异常:
warnings.filterwarnings("error", category=DeprecationWarning) - 临时转异常(推荐用于测试):
with warnings.catch_warnings(): warnings.simplefilter("error", DeprecationWarning) - 若只想对特定模块生效,可加
module=r"mymodule.*"参数做正则匹配 - 错误信息会是:
DeprecationWarning: xxx,Python 报错类型就是DeprecationWarning本身
自定义弃用装饰器时,warnings.warn() 的参数陷阱
很多人写 @deprecated 装饰器,直接传字符串给 warnings.warn(msg),结果警告总显示在装饰器内部文件和行号,而不是调用方。这是默认 stacklevel=1 导致的——它只往上查 1 层调用栈。
正确做法是设 stacklevel=2,让警告指向实际调用位置:
import warnings
from functools import wraps
<p>def deprecated(reason):
def decorator(func):
@wraps(func)
def wrapper(*args, *<em>kwargs):
warnings.warn(
f"{func.<strong>name</strong>} is deprecated: {reason}",
DeprecationWarning,
stacklevel=2 # ← 关键!否则显示 wrapper 行号,不是用户调用行
)
return func(</em>args, **kwargs)
return wrapper
return decorator
</p>
-
stacklevel=2表示跳过wrapper和warn()自身,定位到调用wrapper的那行 - 如果装饰器还套了其他装饰器(比如
@lru_cache),可能需要stacklevel=3,得实测 - 别用
category=UserWarning冒充弃用警告——工具链(如 pyright、ruff)和 CI 都认DeprecationWarning类型
实际项目里,警告捕获的难点不在语法,而在过滤策略的叠加顺序和 stacklevel 的调试成本。尤其是多层装饰器 + pytest + 自定义 warning filter 共存时,警告可能消失、错位或误报。先固定 simplefilter("always"),再逐层放开过滤,比猜更可靠。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











