Как поймать предупреждение numpy, как исключение (не только для тестирования)?

Я должен сделать многочлен Лагранжа в Python для проекта, который я делаю. Я делаю барицентрический стиль, чтобы избежать использования явного for-loop, а не разделенного стиля Ньютона. Проблема в том, что мне нужно поймать деление на ноль, но 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

это предупреждение, которое я хочу поймать. Это должно происходить внутри списка.

4 ответов


кажется, что ваша конфигурация с помощью на 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). Если вы хотите поймать его, вы можете:

  1. использовать numpy.seterr(all='raise') что непосредственно вызовет исключение. Это, однако, изменяет поведение всех операций, поэтому это довольно большая перемена в поведении.
  2. использовать 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!

внимательно прочитайте документацию для 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: 

чтобы добавить немного к ответу @Bakuriu:

Если вы уже знаете, где предупреждение может произойти, то часто чище использовать numpy.errstate менеджер контекста, а не numpy.seterr который обрабатывает все последующие предупреждения одного и того же типа независимо от того, где они происходят в вашем коде:

import numpy as np

a = np.r_[0]
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 

чтобы уточнить ответ @Bakuriu выше, я обнаружил, что это позволяет мне поймать предупреждение во время выполнения аналогично тому, как я бы поймал предупреждение об ошибке, распечатав предупреждение красиво:

import warnings

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

вы, вероятно, сможете поиграть с размещением предупреждений.размещение catch_warnings () в зависимости от того, насколько большой зонтик вы хотите бросить с ошибками ловли таким образом.


удалить предупреждения.filterwarnings и добавить:

numpy.seterr(all='raise')