28.6. warnings — 警告控制 — Python 文档
28.6. 警告 — 警告控制
2.1 版中的新功能。
源代码: :source:`Lib/warnings.py`
警告消息通常在以下情况下发出:警告用户程序中的某些情况很有用,该情况(通常)不保证引发异常和终止程序。 例如,当程序使用过时的模块时,人们可能想要发出警告。
Python 程序员通过调用此模块中定义的 warn() 函数来发出警告。 (C 程序员使用 PyErr_WarnEx();详见 异常处理)。
警告信息通常写入sys.stderr
,但它们的处理方式可以灵活改变,从忽略所有警告到将它们变成异常。 警告的处置可能因警告类别(见下文)、警告消息的文本以及发出警告的源位置而异。 同一源位置的特定警告的重复通常会被抑制。
警告控制有两个阶段:第一,每次发出警告时,确定是否应该发出消息; 接下来,如果要发布消息,则使用用户可设置的挂钩对其进行格式化和打印。
是否发出警告消息的决定是由警告过滤器控制的,它是一个匹配规则和动作的序列。 可以通过调用 filterwarnings() 将规则添加到过滤器,并通过调用 resetwarnings() 重置为其默认状态。
警告信息的打印是通过调用 showwarning() 来完成的,它可能会被覆盖; 此函数的默认实现通过调用 formatwarning() 来格式化消息,自定义实现也可以使用它。
28.6.1. 警告类别
有许多表示警告类别的内置异常。 这种分类对于过滤警告组很有用。 当前定义了以下警告类别类:
班级 | 描述 |
---|---|
Warning
|
这是所有警告类别类的基类。 它是 Exception 的子类。
|
UserWarning
|
warn() 的默认类别。 |
DeprecationWarning
|
有关已弃用功能的警告的基本类别(默认情况下忽略)。 |
SyntaxWarning
|
有关可疑句法特征的警告的基本类别。 |
RuntimeWarning
|
有关可疑运行时功能的警告的基本类别。 |
FutureWarning
|
有关将来会在语义上发生变化的构造的警告的基本类别。 |
PendingDeprecationWarning
|
有关将来不推荐使用的功能的警告的基本类别(默认情况下会忽略)。 |
ImportWarning
|
在导入模块的过程中触发的警告的基本类别(默认忽略)。 |
UnicodeWarning
|
与 Unicode 相关的警告的基本类别。 |
BytesWarning
|
与字节和字节数组相关的警告的基本类别。 |
虽然这些是技术上的内置异常,但它们在此处记录,因为从概念上讲它们属于警告机制。
用户代码可以通过对标准警告类别之一进行子类化来定义其他警告类别。 警告类别必须始终是 Warning
类的子类。
2.7 版本变更:默认忽略 DeprecationWarning
。
28.6.2. 警告过滤器
警告过滤器控制警告是被忽略、显示还是变成错误(引发异常)。
从概念上讲,警告过滤器维护过滤器规范的有序列表; 任何特定警告依次与列表中的每个过滤器规范匹配,直到找到匹配项; 匹配决定匹配的处置。 每个条目都是一个形式为 (action, message, category, module, lineno)的元组,在哪里:
action 是以下字符串之一:
价值
处置
"error"
将匹配的警告变成异常
"ignore"
从不打印匹配警告
"always"
总是打印匹配警告
"default"
为发出警告的每个位置打印第一次出现的匹配警告
"module"
为发出警告的每个模块打印第一次出现的匹配警告
"once"
无论位置如何,只打印第一次出现的匹配警告
message 是一个字符串,其中包含警告消息的开头必须匹配的正则表达式。 表达式被编译为始终不区分大小写。
category 是一个类(
Warning
的子类),警告类别必须是其子类才能匹配。module 是一个包含模块名称必须匹配的正则表达式的字符串。 表达式被编译为区分大小写。
lineno 是一个整数,警告发生的行号必须匹配,或者
0
匹配所有行号。
由于 Warning
类是从内置的 Exception
类派生的,为了将警告转化为错误,我们只需引发 category(message)
。
警告过滤器由传递给 Python 解释器命令行的 -W 选项初始化。 解释器将所有 -W 选项的参数保存在 sys.warnoptions
中,无需解释; warnings 模块在第一次导入时解析这些(无效选项被忽略,在向 sys.stderr
打印消息后)。
28.6.2.1. 默认警告过滤器
默认情况下,Python 安装了几个警告过滤器,它们可以被传递给 -W 的命令行选项和对 filterwarnings() 的调用覆盖。
DeprecationWarning
和PendingDeprecationWarning
和ImportWarning
被忽略。BytesWarning
被忽略,除非 -b 选项被给出一次或两次; 在这种情况下,此警告要么打印 (-b
),要么变成异常 (-bb
)。
28.6.3. 暂时抑制警告
如果您使用的代码知道会引发警告,例如已弃用的函数,但不想看到警告,则可以使用 catch_warnings 上下文管理器抑制警告:
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fxn()
而在上下文管理器中,所有警告都将被忽略。 这允许您使用已知已弃用的代码而不必查看警告,同时不会抑制其他可能不知道其使用已弃用代码的代码的警告。 注意:这只能在单线程应用程序中得到保证。 如果两个或多个线程同时使用 catch_warnings 上下文管理器,则行为未定义。
28.6.4. 测试警告
要测试代码引发的警告,请使用 catch_warnings 上下文管理器。 有了它,您可以暂时改变警告过滤器以方便您的测试。 例如,执行以下操作以捕获所有引发的警告以进行检查:
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
fxn()
# Verify some things
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "deprecated" in str(w[-1].message)
还可以通过使用 error
而不是 always
使所有警告成为例外。 需要注意的一件事是,如果由于 once
/default
规则已经发出警告,那么无论设置什么过滤器,都不会再次看到警告,除非警告与警告相关的注册表已被清除。
一旦上下文管理器退出,警告过滤器就会恢复到进入上下文时的状态。 这可以防止测试在测试之间以意外方式更改警告过滤器并导致不确定的测试结果。 模块中的showwarning()函数也恢复到原来的值。 注意:这只能在单线程应用程序中得到保证。 如果两个或多个线程同时使用 catch_warnings 上下文管理器,则行为未定义。
当测试多个引发相同类型警告的操作时,以确认每个操作引发新警告的方式测试它们很重要(例如 将警告设置为异常并检查操作是否引发异常,检查警告列表的长度在每次操作后是否继续增加,或者在每次新操作之前从警告列表中删除以前的条目)。
28.6.5. 为新版本的 Python 更新代码
默认情况下会忽略仅开发人员感兴趣的警告。 因此,您应该确保使用可见的通常被忽略的警告来测试您的代码。 您可以通过将 -Wd 传递给解释器来从命令行执行此操作(这是 -W default
的简写)。 这将启用对所有警告的默认处理,包括那些默认忽略的警告。 要更改对遇到的警告采取的操作,您只需更改传递给 -W 的参数,例如 -W error
。 有关可能的更多详细信息,请参阅 -W 标志。
要以编程方式执行与 -Wd
相同的操作,请使用:
warnings.simplefilter('default')
确保尽快执行此代码。 这可以防止记录已发出的警告,以免意外影响未来警告的处理方式。
默认情况下忽略某些警告是为了防止用户看到仅开发人员感兴趣的警告。 由于您不一定能够控制用户使用什么解释器来运行他们的代码,因此可能会在您的发布周期之间发布新版本的 Python。 新的解释器版本可能会在您的代码中触发旧解释器中不存在的新警告,例如 DeprecationWarning
表示您正在使用的模块。 虽然您作为开发人员希望收到您的代码正在使用已弃用模块的通知,但对于用户而言,此信息本质上是噪音,对他们没有任何好处。
28.6.6. 可用功能
- warnings.warn(message[, category[, stacklevel]])
发出警告,或者忽略它或引发异常。 category 参数(如果给定)必须是警告类别类(见上文); 默认为
UserWarning
。 或者 message 可以是Warning
实例,在这种情况下,category 将被忽略,而message.__class__
将被使用。 在这种情况下,消息文本将为str(message)
。 如果发出的特定警告被警告过滤器(见上文)更改为错误,则此函数会引发异常。 stacklevel 参数可由 Python 编写的包装函数使用,如下所示:def deprecation(message): warnings.warn(message, DeprecationWarning, stacklevel=2)
这使得警告指向
deprecation()
的调用者,而不是deprecation()
本身的来源(因为后者会破坏警告消息的目的)。
- warnings.warn_explicit(message, category, filename, lineno[, module[, registry[, module_globals]]])
这是 warn() 功能的低级接口,明确传入消息、类别、文件名和行号,以及可选的模块名称和注册表(应该是
__warningregistry__
模块字典)。 模块名默认为去掉.py
的文件名; 如果没有通过注册表,则永远不会抑制警告。 message 必须是一个字符串,而 category 是Warning
或 message 的子类可能是一个Warning
实例,在这种情况下category 将被忽略。module_globals(如果提供)应该是发出警告的代码正在使用的全局命名空间。 (此参数用于支持显示在 zipfiles 或其他非文件系统导入源中找到的模块的源)。
2.5 版更改: 添加 module_globals 参数。
- warnings.warnpy3k(message[, category[, stacklevel]])
发出与 Python 3.x 弃用相关的警告。 警告仅在 Python 以 -3 选项启动时显示。 像 warn() message 必须是一个字符串,而 category 是
Warning
的子类。 warnpy3k() 使用DeprecationWarning
作为默认警告类。2.6 版中的新功能。
- warnings.showwarning(message, category, filename, lineno[, file[, line]])
将警告写入文件。 默认实现调用
formatwarning(message, category, filename, lineno, line)
并将结果字符串写入 file,默认为sys.stderr
。 您可以通过分配给warnings.showwarning
来使用替代实现替换此功能。 line 是要包含在警告信息中的一行源代码; 如果未提供 line,showwarning() 将尝试读取由 filename 和 lineno 指定的行。2.7 版更改: 需要支持 [X37X] 行 参数。
- warnings.formatwarning(message, category, filename, lineno[, line])
以标准方式格式化警告。 这将返回一个字符串,其中可能包含嵌入的换行符并以换行符结尾。 line 是要包含在警告信息中的一行源代码; 如果未提供 line,formatwarning() 将尝试读取由 filename 和 lineno 指定的行。
在 2.6 版更改: 添加了 行 参数。
- warnings.filterwarnings(action[, message[, category[, module[, lineno[, append]]]]])
- 在 警告过滤器规范 列表中插入一个条目。 条目默认插入在前面; 如果 append 为真,则插入到末尾。 这会检查参数的类型,编译 message 和 module 正则表达式,并将它们作为元组插入警告过滤器列表中。 如果两个条目都匹配特定警告,则靠近列表前面的条目会覆盖列表中后面的条目。 省略的参数默认为匹配所有内容的值。
- warnings.simplefilter(action[, category[, lineno[, append]]])
- 在 警告过滤器规范 列表中插入一个简单的条目。 函数参数的含义与filterwarnings()相同,但不需要正则表达式,因为只要类别和行号匹配,插入的过滤器总是匹配任何模块中的任何消息。
- warnings.resetwarnings()
- 重置警告过滤器。 这会丢弃所有先前调用 filterwarnings() 的影响,包括 -W 命令行选项和调用 simplefilter() 的影响。
28.6.7. 可用的上下文管理器
- class warnings.catch_warnings([\*, record=False, module=None])
复制并在退出时恢复警告过滤器和 showwarning() 函数的上下文管理器。 如果 record 参数为 False(默认值),则上下文管理器在输入时返回 None。 如果 record 是 True,则返回一个列表,该列表逐渐填充有自定义 showwarning() 函数(也抑制输出到
sys.stdout
)。 列表中的每个对象都具有与 showwarning() 的参数同名的属性。module 参数采用一个将被使用的模块,而不是在您导入其过滤器将受到保护的 warnings 时返回的模块。 这个论点主要用于测试 warnings 模块本身。
笔记
catch_warnings 管理器通过替换然后恢复模块的 showwarning() 函数和内部过滤器规范列表来工作。 这意味着上下文管理器正在修改全局状态,因此不是线程安全的。
笔记
在 Python 3 中,catch_warnings 的构造函数的参数是仅关键字参数。
2.6 版中的新功能。