Я должен сделать полином Лагранжа в 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
Это предупреждение, которое я хочу поймать. Это должно происходить внутри списка понимания.
Warning: ...
? Попробовать такие вещи, какnp.array([1])/0
я получаю вRuntimeWarning: ...
качестве вывода.Ответы:
Похоже, что ваша конфигурация использует
print
опцию дляnumpy.seterr
:Это означает, что предупреждение, которое вы видите, не является реальным предупреждением, а просто напечатано с некоторыми символами
stdout
(см. Документациюseterr
). Если вы хотите поймать его, вы можете:numpy.seterr(all='raise')
которое напрямую вызовет исключение. Это, однако, меняет поведение всех операций, так что это довольно большое изменение в поведении.numpy.seterr(all='warn')
, что преобразует напечатанное предупреждение в реальное предупреждение, и вы сможете использовать вышеуказанное решение для локализации этого изменения в поведении.Как только у вас появится предупреждение, вы можете использовать
warnings
модуль для управления обработкой предупреждений:Внимательно прочитайте документацию,
filterwarnings
поскольку она позволяет фильтровать только те предупреждения, которые вы хотите, и имеет другие параметры. Я также хотел бы рассмотреть вопрос оcatch_warnings
том, какой контекстный менеджер автоматически сбрасывает исходнуюfilterwarnings
функцию:источник
RuntimeWarning
. Обновил ответ.RuntimeWarning
поднято. Проблема может заключаться в том, что ваша пустая конфигурация используетprint
опцию, которая просто печатает предупреждение, но это не реальное предупреждение, обрабатываемоеwarnings
модулем ... Если это так, вы можете попробовать использоватьnumpy.seterr(all='warn')
и повторить попытку.numpy
, вы не можете использоватьnumpy.seterr(all='error')
,error
должно бытьraise
.Чтобы добавить немного к ответу @ Bakuriu:
Если вы уже знаете, где может появиться предупреждение, то зачастую
numpy.errstate
лучше использовать менеджер контекста, чем тот,numpy.seterr
который обрабатывает все последующие предупреждения одного и того же типа независимо от того, где они появляются в вашем коде:Редактировать:
В моем исходном примере, который у меня был
a = np.r_[0]
, но, видимо, произошло изменение в поведении numpy, так что деление на ноль обрабатывается по-разному в тех случаях, когда числитель - все нули. Например, в numpy 1.16.4:Соответствующие предупреждающие сообщения также различны:
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.
return + Inf и -Inf соответственно согласно стандарту IEE 754Если вы хотите перехватить оба типа ошибок, вы всегда можете передать их
np.errstate(divide='raise', invalid='raise')
, илиall='raise'
если вы хотите вызвать исключение для любого типа ошибки с плавающей запятой.источник
FloatingPointError
, а неZeroDivisionError
.Python 3.6.3
сnumpy==1.16.3
. Не могли бы вы обновить его, пожалуйста?Чтобы уточнить ответ @ Bakuriu выше, я обнаружил, что это позволяет мне перехватывать предупреждение во время выполнения аналогично тому, как я получаю предупреждение об ошибке, красиво распечатывая предупреждение:
Вы, вероятно, сможете поиграть с размещением размещения warnings.catch_warnings () в зависимости от того, какой большой зонт вы хотите использовать для отлова ошибок таким образом.
источник
Удалите warnings.filterwarnings и добавьте:
источник