如何确定地抑制 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.six
和sklearn.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?的主要内容,如果未能解决你的问题,请参考以下文章