如何确定地抑制 Python 中的 DeprecationWarning?

Posted

技术标签:

【中文标题】如何确定地抑制 Python 中的 DeprecationWarning?【英文标题】:How to assuredly suppress a DeprecationWarning in Python? 【发布时间】:2019-06-20 02:57:20 【问题描述】:

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

在我的一个项目中,我使用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()

我正在尝试使用解释器选项 -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

另外,我正在尝试使用 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()

matplotlib 模块和其他一些第三方库也有类似的问题。可能还有其他一些方法(即环境变量),但我不明白为什么这些解决方案不起作用。

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

【问题讨论】:

Sklearn 似乎强制执行警告,请参阅 here 和 here。我没有解决办法。 找到它:诀窍是在导入 sklearn(或使用 sklearn 的依赖项)时使用“with”警告:with warnings.catch_warnings(): warnings.simplefilter("ignore", category=DeprecationWarning) import hdbscan 这将仅禁用此模块的 DeprecationWarning。 @Alex 您介意将your second comment 中嵌入的有用技巧推断为正确答案吗?然后,我们所有人都会将其投票支持平流层。 (想想可能属于你的业力荣耀。 请参阅下面的答案以获取解决方案 【参考方案1】:

有趣的是,即使遵循@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

所以最终,我决定以一种非常老套的方式来做这件事并禁用所有警告,因为我有点厌倦了寻找一种合适的方法来处理它们。 (不仅对于这个库,对于许多其他似乎非常渴望用不可抑制的警告轰炸你的人来说)。

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()

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


更新 1

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


更新 2

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

import logging
logging.captureWarnings(True)

【讨论】:

您检查过您是否真的在使用来自 sklearn 的 Parallel?据我所知,它在新版本中被排除在 sklearn 之外,现在作为单独的包进行维护 @Alex 嘿,谢谢您的回复!我上面显示的代码是我正在使用的精确 sn-p。如果您愿意,可以将其保存到 export.py 并查看它使用来自 sklearn 的 Parallel 但仍显示警告。顺便说一句,当我使用您的代码时,尝试导入 hdbscan 时仍然会收到警告:) 不知道为什么会这样。可能某些版本的 sklearn 在内部某个地方导入 joblib,因此我的上下文管理器没有效果。 不确定,但可能的解释可能是 sklearn 的 this issue,这似乎与父 warnings.filterwarnings 设置混淆 Update 2 成功抑制了旧版本 hvac 的弃用警告【参考方案2】:

根据要求,这是一个单独的帖子的答案:

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

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

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

将此语句放在代码中导入模块的第一个位置很重要,否则它将不起作用。例如。如果我在__init__.py 中加载hdbscan,并且上面的代码块出现在一些也加载hdbscan 的子类中,我仍然会收到DeprecationWarning,因为如果模块/包Python 会忽略任何后续import 语句已加载。

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

[编辑]

正如@devforfu 在 cmets 中指出的那样,上述解决方案不再有效。从Python 3.7DeprecationWarning 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 创建一个伪造的导入,它将覆盖依赖项导入,例如:
try:
    sys.modules['sklearn.externals.six'] = __import__('six')
    sys.modules['sklearn.externals.joblib'] = __import__('joblib')
    import hdbscan
except ImportError:
    import hdbscan

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

【讨论】:

是的,有道理!因此,如果我有一些使用 another 包的包,它会发出警告,我无法抑制它,对吧?除非我先导入这个 另一个 包。 在这种情况下,您可以将加载另一个包一些包添加到上面的代码块中。这就是我所做的:就我而言,hdbscan 正在导入 sklearn,我试图摆脱 check_pickle 警告。所以我添加了hdbscan(不是sklearn,在我的包中不是直接依赖)

以上是关于如何确定地抑制 Python 中的 DeprecationWarning?的主要内容,如果未能解决你的问题,请参考以下文章

选择性地抑制输出python [重复]

根据稍后确定的公式字段抑制标题中的徽标

你将如何抑制低于阈值的值并用 Python 中的字符串替换

如何获取给定文件夹中具有特定扩展名的文件列表?

如何抑制 GROUP_CONCAT MySQL 中的重复项

如何在 Kotlin 中抑制检查式警告