# 如何捕获像异常一样的numpy警告（不仅用于测试）？

## 问题：如何捕获像异常一样的numpy警告（不仅用于测试）？

``````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)]

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. ``````

``Warning: divide by zero encountered in int_scalars``

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.

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

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.

## 回答 0

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

1. 使用`numpy.seterr(all='raise')`它将直接引发异常。但是，这会更改所有操作的行为，因此，这是行为上的很大变化。
2. 使用`numpy.seterr(all='warn')`，可以将打印的警告转换为真实的警告，您将可以使用上述解决方案来本地化此行为更改。

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

``````>>> 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: ``````

It seems that your configuration is using the `print` option for `numpy.seterr`:

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

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

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.

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

``````>>> 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 since it allows you to filter only the warning you want and has other options. I'd also consider looking at which is a context manager which automatically resets the original `filterwarnings` function:

``````>>> 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:
``````

## 回答 1

``````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``````

### 编辑：

``````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``````

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

``````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_`, 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:

``````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.

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 any kind of floating point error.

## 回答 2

``````import warnings

with warnings.catch_warnings():
warnings.filterwarnings('error')
try:
except Warning as e:
print('error found:', e)``````

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:

``````import warnings

with warnings.catch_warnings():
warnings.filterwarnings('error')
try:
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.

## 回答 3

``numpy.seterr(all='raise')``

``````numpy.seterr(all='raise') 