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


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

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