19.2. warnings — 非致命提醒
目的:向用户提供有关运行程序时遇到的问题的非致命警报。
warnings
模块由 PEP 230 引入,作为一种警告程序员关于语言或库功能的变化的方法,以预测 Python 3.0 带来的向后不兼容的变化。它还可用于报告从丢失的库中恢复的配置错误或功能退化。不过, 最好是通过 logging
模块提供面向用户的消息, 因为发送到控制台的警告可能丢失。
由于警告不是致命的,因此程序在运行过程中可能会多次遇到相同的警告情况。warnings
模块抑制来自同一来源的重复消息,以减少一遍又一遍地看到相同警告的烦恼。输出可以根据具体情况进行控制,使用解释器的命令行选项或调用 warnings
中的函数。
类别和过滤
使用内置异常类 Warning
的子类对警告进行分类。exceptions
模块的在线文档中描述了几个标准值,可以通过 Warning
的子类添加自定义警告。
警告根据 filter 设置进行处理。过滤器由五部分组成:action
,message
,category
,module
和 line number
。过滤器的 message
部分是一个正则表达式,用于匹配警告文本。category
是异常类的名称。module
包含一个正则表达式,用于匹配生成警告的模块名称。并且 line number
可用于更改特定事件发生时的处理。
生成警告时,会将其与所有已注册的过滤器进行比较。匹配的第一个过滤器控制警告所采取的操作。如果没有匹配的过滤器,则采用默认操作。过滤机制理解的操作列在下表中。
警告过滤器操作
动作 | 执行内容 |
---|---|
error | 将 warning 转换为 exception |
ignore | 关闭 Warning |
always | 永远提示 Warning |
default | 在每一个位置 warning 第一次生成时打印出来 |
module | 在每一个 Module 里 warning 第一次生成时打印出来 |
once | warning 第一次生成时打印出来 |
生成警告
发出警告的最简单方法是使用消息作为参数调用 warn()
。
warnings_warn.py
import warnings
print('Before the warning')
warnings.warn('This is a warning message')
print('After the warning')
然后,当程序运行时,将打印该消息。
$ python3 -u warnings_warn.py
Before the warning
warnings_warn.py:13: UserWarning: This is a warning message
warnings.warn('This is a warning message')
After the warning
即使打印了警告,默认行为是继续经过该点并运行程序的其余部分。 可以使用过滤器更改该行为。
warnings_warn_raise.py
import warnings
warnings.simplefilter('error', UserWarning)
print('Before the warning')
warnings.warn('This is a warning message')
print('After the warning')
在这个例子中, simplefilter()
函数在内部过滤器列表中添加一个条目,告诉 warnings
模块在发出 UserWarning
警告时引发异常。
$ python3 -u warnings_warn_raise.py
Before the warning
Traceback (most recent call last):
File "warnings_warn_raise.py", line 15, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
也可以通过使用解释器的 -W
选项从命令行控制过滤器行为。 将过滤器属性指定为一个字符串,其中五个部分(操作、消息、类别、模块和行号)以冒号( :
)分隔。 例如,如果运行 warnings_warn.py
并将过滤器设置为在 UserWarning
上引发错误,则会产生异常。
$ python3 -u -W "error::UserWarning::0" warnings_warn.py
Before the warning
Traceback (most recent call last):
File "warnings_warn.py", line 13, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
由于 message
和 module
的字段留空,因此它们被解释为匹配任何内容。
使用模式过滤
要以编程方式过滤更复杂的规则,请使用 filterwarnings()
。 例如,要根据消息文本的内容进行过滤,请将正则表达式模式作为 message
参数。
warnings_filterwarnings_message.py
import warnings
warnings.filterwarnings('ignore', '.*do not.*',)
warnings.warn('Show this message')
warnings.warn('Do not show this message')
该模式包含「 do not
」,但实际消息使用「 Do not
」。 模式匹配,因为始终编译正则表达式以查找不区分大小写的匹配项。
$ python3 warnings_filterwarnings_message.py
warnings_filterwarnings_message.py:14: UserWarning: Show this
message
warnings.warn('Show this message')
下面的示例程序会生成两个警告。
warnings_filter.py
import warnings
warnings.warn('Show this message')
warnings.warn('Do not show this message')
可以使用命令行上的filter参数忽略其中一个警告。
$ python3 -W "ignore:do not:UserWarning::0" warnings_filter.py
warnings_filter.py:12: UserWarning: Show this message
warnings.warn('Show this message')
相同的模式匹配规则适用于包含生成警告的调用的源模块的名称。 通过将模块名称作为模式传递给 module
参数来抑制来自 warnings_filter
模块的所有消息。
warnings_filterwarnings_module.py
import warnings
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filter',
)
import warnings_filter
由于过滤器已就位,因此在导入 warnings_filter
时不会发出警告。
$ python3 warnings_filterwarnings_module.py
要仅抑制 warnings_filter
第 13 行的消息,请将行号包含为 filterwarnings()
的最后一个参数。 使用源文件中的实际行号来限制过滤器,或使用 0
使过滤器适用于所有出现的消息。
warnings_filterwarnings_lineno.py
import warnings
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filter',
13,
)
import warnings_filter
模式匹配任何消息,因此重要的参数是模块名称和行号。
$ python3 warnings_filterwarnings_lineno.py
.../warnings_filter.py:12: UserWarning: Show this message
warnings.warn('Show this message')
重复警告
默认情况下,大多数类型的警告仅在第一次出现在给定位置时打印,其中「 location 」由生成警告的模块和行号组合定义。
warnings_repeated.py
import warnings
def function_with_warning():
warnings.warn('This is a warning!')
function_with_warning()
function_with_warning()
function_with_warning()
此示例多次调用相同的函数,但会生成一个警告。
$ python3 warnings_repeated.py
warnings_repeated.py:14: UserWarning: This is a warning!
warnings.warn('This is a warning!')
「 once 」
动作可用于抑制来自不同位置的同一消息的实例。
warnings_once.py
import warnings
warnings.simplefilter('once', UserWarning)
warnings.warn('This is a warning!')
warnings.warn('This is a warning!')
warnings.warn('This is a warning!')
将保存所有警告的消息文本,并且仅打印唯一消息。
$ python3 warnings_once.py
warnings_once.py:14: UserWarning: This is a warning!
warnings.warn('This is a warning!')
类似地,「 module 」
将禁止来自同一模块的重复消息,无论哪个行号。
其他消息传递函数
通常警告会打印到 sys.stderr
。通过替换 warnings
模块中的 showwarning()
函数来改变这种行为。 例如,要将警告发送到日志文件而不是标准错误流,请将 showwarning()
替换为记录警告的函数。
warnings_showwarning.py
import warnings
import logging
def send_warnings_to_log(message, category, filename, lineno,
file=None, line=None):
logging.warning(
'%s:%s: %s:%s',
filename, lineno,
category.__name__, message,
)
logging.basicConfig(level=logging.INFO)
old_showwarning = warnings.showwarning
warnings.showwarning = send_warnings_to_log
warnings.warn('message')
当调用 warn()
时,警告将与其余的日志消息一起发出。
$ python3 warnings_showwarning.py
WARNING:root:warnings_showwarning.py:28: UserWarning:message
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。