Python 我如何捕捉 numpy 警告,就像它是一个例外(不仅仅是为了测试)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15933741/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 21:24:42  来源:igfitidea点击:

How do I catch a numpy warning like it's an exception (not just for testing)?

pythonexceptionnumpywarningsdivide-by-zero

提问by John K.

I have to make a Lagrange polynomial in Python for a project I'm doing. I'm doing a barycentric style one to avoid using an explicit for-loop as opposed to a Newton's divided difference style one. The problem I have is that I need to catch a division by zero, but Python (or maybe numpy) just makes it a warning instead of a normal exception.

我必须在 Python 中为我正在做的项目创建一个拉格朗日多项式。我正在做一种以重心为中心的风格,以避免使用显式的 for 循环,而不是牛顿的划分差异风格。我遇到的问题是我需要捕捉除以零,但是 Python(或者可能是 numpy)只是使它成为一个警告而不是一个正常的异常。

So, what I need to know how to do is to catch this warning as if it were an exception. The related questions to this I found on this site were answered not in the way I needed. Here's my code:

所以,我需要知道如何做的是捕捉这个警告,就好像它是一个例外。我在本网站上找到的与此相关的问题没有以我需要的方式回答。这是我的代码:

import numpy as np
import matplotlib.pyplot as plt
import warnings

class Lagrange:
    def __init__(self, xPts, yPts):
        self.xPts = np.array(xPts)
        self.yPts = np.array(yPts)
        self.degree = len(xPts)-1 
        self.weights = np.array([np.product([x_j - x_i for x_j in xPts if x_j != x_i]) for x_i in xPts])

    def __call__(self, x):
        warnings.filterwarnings("error")
        try:
            bigNumerator = np.product(x - self.xPts)
            numerators = np.array([bigNumerator/(x - x_j) for x_j in self.xPts])
            return sum(numerators/self.weights*self.yPts) 
        except Exception, e: # Catch division by 0. Only possible in 'numerators' array
            return yPts[np.where(xPts == x)[0][0]]

L = Lagrange([-1,0,1],[1,0,1]) # Creates quadratic poly L(x) = x^2

L(1) # This should catch an error, then return 1. 

When this code is executed, the output I get is:

执行此代码时,我得到的输出是:

Warning: divide by zero encountered in int_scalars

That's the warning I want to catch. It should occur inside the list comprehension.

这就是我想抓住的警告。它应该出现在列表理解中。

采纳答案by Bakuriu

It seems that your configuration is using the printoption for numpy.seterr:

您的配置似乎正在使用以下print选项numpy.seterr

>>> import numpy as np
>>> np.array([1])/0   #'warn' mode
__main__:1: RuntimeWarning: divide by zero encountered in divide
array([0])
>>> np.seterr(all='print')
{'over': 'warn', 'divide': 'warn', 'invalid': 'warn', 'under': 'ignore'}
>>> np.array([1])/0   #'print' mode
Warning: divide by zero encountered in divide
array([0])

This means that the warning you see is nota real warning, but it's just some characters printed to stdout(see the documentation for seterr). If you want to catch it you can:

这意味着您看到的警告不是真正的警告,而只是打印的一些字符stdout(请参阅 的文档seterr)。如果你想抓住它,你可以:

  1. Use numpy.seterr(all='raise')which will directly raise the exception. This however changes the behaviour of all the operations, so it's a pretty big change in behaviour.
  2. Use numpy.seterr(all='warn'), which will transform the printed warning in a real warning and you'll be able to use the above solution to localize this change in behaviour.
  1. 使用numpy.seterr(all='raise')which 将直接引发异常。然而,这改变了所有操作的行为,所以这是一个相当大的行为变化。
  2. 使用numpy.seterr(all='warn'),这会将打印的警告转换为真正的警告,您将能够使用上述解决方案来定位这种行为变化。

Once you actually have a warning, you can use the warningsmodule to control how the warnings should be treated:

一旦您真正warnings收到警告,您可以使用该模块来控制应如何处理警告:

>>> import warnings
>>> 
>>> warnings.filterwarnings('error')
>>> 
>>> try:
...     warnings.warn(Warning())
... except Warning:
...     print 'Warning was raised as an exception!'
... 
Warning was raised as an exception!

Read carefully the documentation for filterwarningssince it allows you to filter only the warning you want and has other options. I'd also consider looking at catch_warningswhich is a context manager which automatically resets the original filterwarningsfunction:

仔细阅读文档,filterwarnings因为它允许您仅过滤所需的警告并具有其他选项。我还会考虑查看catch_warnings哪个是自动重置原始filterwarnings功能的上下文管理器:

>>> import warnings
>>> with warnings.catch_warnings():
...     warnings.filterwarnings('error')
...     try:
...         warnings.warn(Warning())
...     except Warning: print 'Raised!'
... 
Raised!
>>> try:
...     warnings.warn(Warning())
... except Warning: print 'Not raised!'
... 
__main__:2: Warning: 

回答by ali_m

To add a little to @Bakuriu's answer:

为@Bakuriu 的回答添加一点:

If you already know where the warning is likely to occur then it's often cleaner to use the numpy.errstatecontext manager, rather than numpy.seterrwhich treats all subsequent warnings of the same type the same regardless of where they occur within your code:

如果您已经知道警告可能发生的位置,那么使用numpy.errstate上下文管理器通常会更清晰,而不是 numpy.seterr将相同类型的所有后续警告视为相同,而不管它们出现在您的代码中的位置:

import numpy as np

a = np.r_[1.]
with np.errstate(divide='raise'):
    try:
        a / 0   # this gets caught and handled as an exception
    except FloatingPointError:
        print('oh no!')
a / 0           # this prints a RuntimeWarning as usual


Edit:

编辑:

In my original example I had a = np.r_[0], but apparently there was a change in numpy's behaviour such that division-by-zero is handled differently in cases where the numerator is all-zeros. For example, in numpy 1.16.4:

在我的原始示例中,我有a = np.r_[0],但显然 numpy 的行为发生了变化,因此在分子全为零的情况下,除以零的处理方式有所不同。例如,在 numpy 1.16.4 中:

all_zeros = np.array([0., 0.])
not_all_zeros = np.array([1., 0.])

with np.errstate(divide='raise'):
    not_all_zeros / 0.  # Raises FloatingPointError

with np.errstate(divide='raise'):
    all_zeros / 0.  # No exception raised

with np.errstate(invalid='raise'):
    all_zeros / 0.  # Raises FloatingPointError

The corresponding warning messages are also different: 1. / 0.is logged as RuntimeWarning: divide by zero encountered in true_divide, whereas 0. / 0.is logged as RuntimeWarning: invalid value encountered in true_divide. I'm not sure why exactly this change was made, but I suspect it has to do with the fact that the result of 0. / 0.is not representable as a number (numpy returns a NaN in this case) whereas 1. / 0.and -1. / 0.return +Inf and -Inf respectively, per the IEE 754 standard.

相应的警告消息也不同:1. / 0.记录为RuntimeWarning: divide by zero encountered in true_divide,而0. / 0.记录为RuntimeWarning: invalid value encountered in true_divide。我不知道到底为什么这种变化作出的,但我怀疑它有与事实结果做0. / 0.是不能表示为数字(numpy的回报为NaN在这种情况下),而1. / 0.-1. / 0.分别返回+ Inf文件和-Inf , 根据 IEE 754 标准。

If you want to catch both types of error you can always pass np.errstate(divide='raise', invalid='raise'), or all='raise'if you want to raise an exception on anykind of floating point error.

如果你想捕获这两种类型的错误,你总是可以通过np.errstate(divide='raise', invalid='raise'),或者all='raise'如果你想在任何类型的浮点错误上引发异常。

回答by ntk4

To elaborate on @Bakuriu's answer above, I've found that this enables me to catch a runtime warning in a similar fashion to how I would catch an error warning, printing out the warning nicely:

为了详细说明上面@Bakuriu 的回答,我发现这使我能够以与捕获错误警告类似的方式捕获运行时警告,并很好地打印出警告:

import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        answer = 1 / 0
    except Warning as e:
        print('error found:', e)

You will probably be able to play around with placing of the warnings.catch_warnings() placement depending on how big of an umbrella you want to cast with catching errors this way.

您可能可以根据您希望以这种方式捕捉错误的伞的大小来放置 warnings.catch_warnings() 的位置。

回答by Shital Shah

Remove warnings.filterwarnings and add:

删除 warnings.filterwarnings 并添加:

numpy.seterr(all='raise')