我要用Python做一个拉格朗日多项式来做一个项目。我用的是重心式的1来避免使用显式的for循环这与牛顿分差式的1相反。我遇到的问题是,我需要捕获除以0的除法,但Python(或者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)[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. 

当执行这段代码时,我得到的输出是:

Warning: divide by zero encountered in int_scalars

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


当前回答

给@Bakuriu的回答补充一点:

如果您已经知道可能出现警告的位置,那么使用numpy通常会更简洁。错误的上下文管理器,而不是numpy。seterer,它对所有相同类型的后续警告都一视同仁,不管它们出现在代码中的哪个位置:

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

编辑:

在我原来的例子中,a = np。R_[0],但显然numpy的行为发生了变化,在分子为全0的情况下,被0除的处理方式有所不同。例如,在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

相应的警告信息也有所不同:1.安全;/ 0。日志记录为RuntimeWarning:除以true_divide中遇到的0,而0。/ 0。日志记录为RuntimeWarning:在true_divide中遇到的无效值。我不确定为什么会做出这样的改变,但我怀疑这与结果为0。/ 0。不能表示为数字(numpy在这种情况下返回NaN),而1。/ 0。和1。/ 0。根据IEE 754标准,分别返回+Inf和-Inf。

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

其他回答

给@Bakuriu的回答补充一点:

如果您已经知道可能出现警告的位置,那么使用numpy通常会更简洁。错误的上下文管理器,而不是numpy。seterer,它对所有相同类型的后续警告都一视同仁,不管它们出现在代码中的哪个位置:

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

编辑:

在我原来的例子中,a = np。R_[0],但显然numpy的行为发生了变化,在分子为全0的情况下,被0除的处理方式有所不同。例如,在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

相应的警告信息也有所不同:1.安全;/ 0。日志记录为RuntimeWarning:除以true_divide中遇到的0,而0。/ 0。日志记录为RuntimeWarning:在true_divide中遇到的无效值。我不确定为什么会做出这样的改变,但我怀疑这与结果为0。/ 0。不能表示为数字(numpy在这种情况下返回NaN),而1。/ 0。和1。/ 0。根据IEE 754标准,分别返回+Inf和-Inf。

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

删除警告。过滤警告和添加:

numpy.seterr(all='raise')

看起来你的配置正在使用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])

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

使用numpy.seterr(all='raise')将直接引发异常。然而,这改变了所有操作的行为,所以这是一个相当大的行为变化。 使用numpy.seterr(all='warn'),它将把打印的警告转换为真正的警告,您将能够使用上述解决方案来本地化这种行为变化。

一旦你真的有一个警告,你可以使用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!

仔细阅读过滤器警告的文档,因为它允许您只过滤您想要的警告,并且有其他选项。我也会考虑查看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: 

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

import warnings

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

你可能可以根据你想用这种方法捕获错误的保护伞的大小来设置warnings.catch_warnings()的位置。