如何确定抑制 Python 中的 DeprecationWarning?

dev*_*rfu 5 python warnings

我相信这个问题已经被提出很多次了,但我有一个特定的用例,我无法用网络上描述的许多方法解决这个问题。

在我的一个项目中,我正在使用joblib库,它显示是DeprecationWarning因为它在imp内部某处使用了库:

from sklearn.externals.joblib import Parallel, delayed

def main():
    xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
    print(sum(xs))

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

我试图用解释器选项过滤掉警告,-W但它没有帮助:

$ python -W ignore example.py                                                                                                                   
[...]/lib/python3.7/site-packages/sklearn/externals/joblib/externals/cloudpickle/cloudpickle.py:47:
DeprecationWarning: the imp module is deprecated in favour of importlib; 
see the module's documentation for alternative uses import imp
55
Run Code Online (Sandbox Code Playgroud)

另外,我正在尝试使用warnings模块进行显式过滤,但它也无济于事:

import warnings
warnings.simplefilter('ignore', category=DeprecationWarning)
from sklearn.externals.joblib import Parallel, delayed

def main():
    xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
    print(sum(xs))

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

我在matplotlib模块和其他一些第三方库中遇到了类似的问题。可能还有其他一些方法(即 env vars),但我不明白为什么这些解决方案不起作用。

谁能解释警告系统在 Python 中的实际工作原理?第三方库有可能故意覆盖客户端的警告设置吗?我会说这个问题对我来说是最晦涩的话题之一。

dev*_*rfu 5

有趣的是,即使遵循@Alex 的建议,我仍然有警告输出,如下所示:

import warnings
with warnings.catch_warnings():
    warnings.simplefilter('ignore', category=DeprecationWarning)
    from sklearn.externals.joblib import Parallel, delayed

def main():
    xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
    print(sum(xs))

if __name__ == '__main__':
    main()

# $ python -W ignore example.py
# [...]
# DeprecationWarning: the imp module is deprecated in favour of importlib; 
# see the module's documentation for alternative uses
#  import imp
# 55
Run Code Online (Sandbox Code Playgroud)

所以最终,我决定以一种非常hacky的方式来做并禁用所有警告,因为我有点厌倦了寻找一种正确的方法来处理它们。(不仅对于这个库,而且对于许多其他似乎非常渴望用不可抑制的警告轰炸您的库)。

import warnings
def noop(*args, **kargs): pass
warnings.warn = noop
from sklearn.externals.joblib import Parallel, delayed

def main():
    xs = Parallel()(delayed(lambda x: x**2)(i) for i in range(1, 6))
    print(sum(xs))

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

如果我错误地使用了@Alex 的建议,或者你们中的一些人有更好的解决方案,我很乐意接受它作为答案。


更新 1

好吧,似乎很难影响在包内部某处提出的警告。所以可能最简单的事情就是替换warnings.warnnoop,或者以某种方式提前导入内部依赖项并使用上下文管理器抑制它们。


更新 2

前段时间我发现了另一种处理警告的可能方法。您可以将它们重定向到日志记录。如果没有明确配置记录器,这些警告基本上会被抑制。它适用于 Jupyter 和我测试过的一些库。

import logging
logging.captureWarnings(True)
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 5

根据要求,这里的答案作为单独的帖子:

诀窍是在导入时使用“with”警告sklearn(或使用 sklearn 的依赖项,在我的情况下是hdbscan包):

with warnings.catch_warnings():
    # filter sklearn\externals\joblib\parallel.py:268:
    # DeprecationWarning: check_pickle is deprecated
    warnings.simplefilter("ignore", category=DeprecationWarning)
    import hdbscan
Run Code Online (Sandbox Code Playgroud)

这将仅为此模块禁用 DeprecationWarning(因为warnings-modification 附加到 with-block)。

将此语句放在导入模块的代码中的第一个位置很重要,否则它将无法工作。例如,如果我是装hdbscan__init__.py,而上面的代码块出现在一些子类,还加载hdbscan,我仍然会得到DeprecationWarning因为Python忽略任何后续import语句如果模块/包已经被加载。

因此,joblib\parallel.py从线性代码的角度来看,检查哪些模块/包使用以及它们最早加载到 python 对象堆的位置很重要。

[编辑]

正如@devforfu 在评论中指出的那样,上述解决方案(不再)起作用。从Python 3.7 开始, 我再次研究了这个问题DeprecationWarning is once again shown by default when triggered directly by code in __main__.。此外,ignore当依赖项显式加载某个其他包的折旧模块时,警告似乎不起作用。

这就是我的hdbscan示例中出现的情况,它加载了折旧的模块sklearn.external.sixsklearn.externals.joblib.

以下是最终解决这个烦人问题的方法:

  • 确保您已经明确安装了折旧的独立软件包,例如 conda install -c conda-forge joblib six
  • 创建一个将覆盖依赖项导入的假导入,例如:
with warnings.catch_warnings():
    # filter sklearn\externals\joblib\parallel.py:268:
    # DeprecationWarning: check_pickle is deprecated
    warnings.simplefilter("ignore", category=DeprecationWarning)
    import hdbscan
Run Code Online (Sandbox Code Playgroud)

如果没有导入错误,将使用独立 6 和 joblib。否则,例如,如果用户没有安装 6 或 joblib,该程序仍然可以工作(因为它从 sklearn.externals 加载两个模块),但它会显示折旧警告。