warnings - Warning control#

Warnings 是 Python 中负责警告的模块. 它通常是发出一些警告信息, 但不会阻止程序运行.

[3]:
import warnings

Define Custom Warning#

[13]:
class MyWarning(Warning):
    pass

Raise Warning#

[16]:
def my_func():
    warnings.warn("My warning in my_func()!", MyWarning)
    print("run my_func()")
[18]:
print("before")
my_func()
print("after")
before
run my_func()
after
/var/folders/3y/7t5ll4sn6x76g8rhfqlc36dw0000gn/T/ipykernel_58584/1986793376.py:2: MyWarning: My warning in my_func()!
  warnings.warn("My warning in my_func()!", MyWarning)

Temporarily Suppressing Warnings#

有的时候你知道一段代码会抛出 warning, 而且你很清楚这个 warning 是可以被忽略的, 你不希望打印出 warning 信息. 但是你又不希望因为禁用了全部的 warning 导致在这一段代码外应该抛出 warning 的地方没有抛出. 你希望用类似 try ... except ExpectedException 这样的方式不仅精确控制忽略 warning 的代码块, 并且还能清晰的指定只忽略特定 的 warning 类型.

warning 模块提供了两个 API 可以做到这一点:

  • warnings.catch_warnings: 它是一个 context manager, 能够暂时的修改全局的 warning 的设置并在推出 context 后自动恢复原有设置.

  • warnings.simplefilter: 它是一个函数, 能修改全局的 warning filter 的设置. 其中它有两个关键参数, actioncategory. action 定义了 match 到特定 warning 后应该怎么处理 (请参考 Warning Filter), 例如有: “ignore” 表示不打印 warning, “error” 表示将其转化为 Exception. 等等. 而 category 则定义了要过滤的 warning 类. 所有指定类的子类都会被过滤掉. 这根 try, except 机制一样. 其中 Warning 是所有 warning 类的基类.

值得注意的是在 3.11 中 catch_warnings 加入了 action, category 参数. 也就是说 3.11 之前你需要两个 API 配合使用, 而 3.11 后则只需要 catch_warnings 即可.

Reference:

You won’t see the warning.

[21]:
with warnings.catch_warnings():
    # ignore it
    warnings.simplefilter("ignore", MyWarning)
    my_func()
run my_func()

Without context manager, you still see the warning.

[22]:
my_func()
run my_func()
/var/folders/3y/7t5ll4sn6x76g8rhfqlc36dw0000gn/T/ipykernel_58584/1986793376.py:2: MyWarning: My warning in my_func()!
  warnings.warn("My warning in my_func()!", MyWarning)

You still see the warning, because the category is wrong.

[23]:
with warnings.catch_warnings():
    # wrong Warning type
    warnings.simplefilter("ignore", category=DeprecationWarning)
    my_func()
run my_func()
/var/folders/3y/7t5ll4sn6x76g8rhfqlc36dw0000gn/T/ipykernel_58584/1986793376.py:2: MyWarning: My warning in my_func()!
  warnings.warn("My warning in my_func()!", MyWarning)

Testing Warnings#

有的时候你需要测试你的代码, 看看 warning 是不是真的被抛出了. 在 pytest 框架中有一个 with pytest.raises(YourException) 的 API 可以做到测试指定异常是不是被抛出了. 类似地, warnings.catch_warnings 也能做到这一点.

Reference:

[24]:
with warnings.catch_warnings(record=True) as w:
    # Cause all warnings always to be triggered.
    warnings.simplefilter("always", category=MyWarning)
    # Trigger a warning.
    my_func()
    # Verify some things
    assert len(w) == 1
run my_func()
[ ]: