19.2. warnings — 非致命提醒

未匹配的标注

目的:向用户提供有关运行程序时遇到的问题的非致命警报。

warnings 模块由 PEP 230 引入,作为一种警告程序员关于语言或库功能的变化的方法,以预测 Python 3.0 带来的向后不兼容的变化。它还可用于报告从丢失的库中恢复的配置错误或功能退化。不过, 最好是通过 logging 模块提供面向用户的消息, 因为发送到控制台的警告可能丢失。

由于警告不是致命的,因此程序在运行过程中可能会多次遇到相同的警告情况。warnings 模块抑制来自同一来源的重复消息,以减少一遍又一遍地看到相同警告的烦恼。输出可以根据具体情况进行控制,使用解释器的命令行选项或调用 warnings 中的函数。

类别和过滤

使用内置异常类 Warning 的子类对警告进行分类。exceptions 模块的在线文档中描述了几个标准值,可以通过 Warning 的子类添加自定义警告。

警告根据 filter 设置进行处理。过滤器由五部分组成:actionmessagecategorymoduleline 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

由于 messagemodule 的字段留空,因此它们被解释为匹配任何内容。

使用模式过滤

要以编程方式过滤更复杂的规则,请使用 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

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/warnings...

译文地址:https://learnku.com/docs/pymotw/warnings...

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~