我相信这个问题已经被提出很多次了,但我有一个特定的用例,我无法用网络上描述的许多方法解决这个问题。
在我的一个项目中,我正在使用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 中的实际工作原理?第三方库有可能故意覆盖客户端的警告设置吗?我会说这个问题对我来说是最晦涩的话题之一。
有趣的是,即使遵循@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.warn为noop,或者以某种方式提前导入内部依赖项并使用上下文管理器抑制它们。
更新 2
前段时间我发现了另一种处理警告的可能方法。您可以将它们重定向到日志记录。如果没有明确配置记录器,这些警告基本上会被抑制。它适用于 Jupyter 和我测试过的一些库。
import logging
logging.captureWarnings(True)
Run Code Online (Sandbox Code Playgroud)
根据要求,这里的答案作为单独的帖子:
诀窍是在导入时使用“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.six和sklearn.externals.joblib.
以下是最终解决这个烦人问题的方法:
conda install -c conda-forge joblib sixwith 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 加载两个模块),但它会显示折旧警告。