Лучше использовать исключение или код возврата в Python? - PullRequest
39 голосов
/ 20 июля 2009

Возможно, вы знаете эту рекомендацию от Microsoft об использовании исключений в .NET:

Вопросы производительности

...

Бросать исключения только для чрезвычайные условия, ...

Кроме того, не выбрасывайте исключение когда достаточно кода возврата ...

(Полный текст см. На http://msdn.microsoft.com/en-us/library/system.exception.aspx.)

Для сравнения, вы бы порекомендовали то же самое для кода Python?

Ответы [ 6 ]

34 голосов
/ 20 июля 2009

Pythonic, что нужно сделать, это поднимать и обрабатывать исключения. Отличная книга «Python в двух словах» обсуждает это в «Стратегиях проверки ошибок» в главе 6.

В книге обсуждается EAFP («проще просить прощения, чем разрешения») и LBYL («смотри, прежде чем прыгнуть»).

Итак, чтобы ответить на ваш вопрос:

Нет, я не рекомендовал бы то же самое для кода Python. Я предлагаю вам прочитать главу 6 Python в двух словах .

10 голосов
/ 20 июля 2009

Лучший способ понять исключения - это ", если ваш метод не может сделать то, что написано в его названии, бросить ." Мое личное мнение таково, что этот совет должен применяться одинаково как к .NET, так и к Python.

Ключевое отличие состоит в том, что у вас есть методы, которые часто не могут делать то, что, как говорит их имя, они должны делать, например, анализировать строки как целые числа или извлекать запись из базы данных. Стиль C # состоит в том, чтобы избежать исключения в первую очередь:

int i;
if (Int32.TryParse(myString, out i)) {
    doWhatever(i);
}
else {
    doWhatever(0);
}

тогда как Python гораздо легче справляется с такими вещами:

try:
    i = int(myString)
except ValueError:
    i = 0
doWhatever(i);
8 голосов
/ 20 июля 2009

Обычно Python ориентирован на выразительность.
Я бы применил тот же принцип здесь: обычно вы ожидаете, что функция вернет результат (в соответствии со своим именем!), А не код ошибки.
По этой причине обычно лучше вызывать исключение, чем возвращать код ошибки.

Однако то, что указано в статье MSDN, применимо и к Python, и оно на самом деле не связано с возвратом кода ошибки вместо исключения.
Во многих случаях вы можете увидеть обработку исключений, используемую для обычного управления потоком и для обработки ожидаемых ситуаций. В определенных условиях это оказывает огромное влияние на производительность; во всех средах это оказывает большое влияние на выразительность и ремонтопригодность программы.

Исключения предназначены для исключительных ситуаций, которые находятся за пределами нормального потока программы; если вы ожидаете, что что-то случится, вы должны обработать напрямую, а затем поднять все, что вы не можете ожидать / обработать.

Конечно, это не рецепт, а только эвристика; окончательное решение всегда зависит от разработчика и от контекста и не может быть указано в фиксированном наборе руководящих принципов - и это гораздо более справедливо для обработки исключений.

8 голосов
/ 20 июля 2009

В Python исключения не очень дороги, как и в некоторых других языках, поэтому я не рекомендую пытаться избегать исключений. Но если вы генерируете исключение, вы, как правило, хотите перехватить его где-нибудь в вашем коде, за исключением случаев, когда происходит фатальная ошибка.

4 голосов
/ 28 июня 2013

Я провел простой эксперимент, чтобы сравнить производительность создания исключений со следующим кодом:

from functools import wraps
from time import time
import logging

def timed(foo):
    @wraps(foo)
    def bar(*a, **kw):
        s = time()
        foo(*a, **kw)
        e = time()
        print '%f sec' % (e - s)
    return bar

class SomeException(Exception):
    pass

def somefunc(_raise=False):
    if _raise:
        raise SomeException()
    else:
        return

@timed
def test1(_reps):
    for i in xrange(_reps):
        try:
            somefunc(True)
        except SomeException:
            pass

@timed
def test2(_reps):
    for i in xrange(_reps):
        somefunc(False)

def main():

    test1(1000000)
    test2(1000000)

    pass

if __name__ == '__main__':
    main()

Со следующими результатами:

  • Повышение исключений: 3,142000 с
  • Использование return: 0,383000 с

Исключения примерно в 8 раз медленнее, чем при использовании return.

3 голосов
/ 13 августа 2009

Я думаю, стоит ли возвращать код ошибки или выбрасывать исключение, и сравнение между языками может быть полезным и информативным. Я предполагаю, что очень обобщенным ответом на эту проблему является просто соображение: набор допустимых возвращаемых значений для любой функции должен быть как можно меньшим и настолько большим, насколько необходимо .

Как правило, это будет означать, что если данный метод возвращает целое число в одном тестовом случае, пользователи могут по праву ожидать, что метод всегда вернет целое число или , бросив исключение. Но, конечно, концептуально самый простой способ не всегда является лучшим способом справиться с вещами.

Возвращаемое значение наименьшего сюрприза обычно None; и если вы посмотрите на это, вы увидите, что именно семантика None лицензирует его использование по всем направлениям: это одноэлементное неизменное значение, которое во многих случаях оценивается как False или запрещает дальнейшие вычисления - без конкатенации, без арифметики. Поэтому, если вы решили написать метод frob(x), который возвращает число для строкового ввода, и None для нечисловых строк и любого другого ввода, и вы используете это внутри выражения, такого как a=42+frob('foo'), вы все равно получите Исключение очень близко к точке, где произошли поддельные вещи. Конечно, если вы добавите frob('foo') в столбец базы данных, который не был определен с NOT NULL, вы можете столкнуться с проблемами, возможно, спустя месяцы. Это может или не может быть оправдано.

Так что в большинстве случаев, когда вы, например, вы хотите получить число из строки, используя somwething наподобие float(x) или int(x), так как эти встроенные функции вызовут исключение, когда не будет введен усваиваемый ввод. Если это не подходит вашему варианту использования, рассмотрите возможность возврата None из пользовательского метода; в основном это возвращаемое значение говорит потребителям, что «извините, я не смог понять ваш вклад». Но вы захотите делать это только в том случае, если вы точно знаете, что продолжение вашей программы имеет смысл с этого момента.

Видите ли, я только что узнал, как превратить каждое уведомление, предупреждение и сообщение об ошибке в потенциально исключающую остановку показа в PHP. Меня просто сводит с ума, что опечатка в имени переменной генерируется в стандартной конфигурации PHP, - только уведомление для пользователя . Это так что плохо. Программа просто продолжает делать что-то с программным кодом, который вообще не имеет смысла! Я не могу поверить, что люди находят эту особенность.

Точно так же следует рассматривать это так: если в любой данный момент времени можно с разумными затратами утверждать, что выполнение фрагмента кода больше не имеет смысла - поскольку значения отсутствуют, их нет граничит, или имеет неожиданный тип, или когда ресурсы, такие как соединение с базой данных, перестали работать - крайне важно минимизировать головную боль отладки, прервать выполнение и передать управление на любой уровень в коде, который чувствует себя способным справиться с неудачей.

Опыт показывает, что воздерживаться от ранних действий и позволять фиктивным значениям проникать в ваши данные - это не что иное, как затруднить отладку кода. Так много примеров чрезмерного усердного приведения типов: допустимо добавлять целые числа в числа с плавающей точкой. Допускать добавление к числу строки без цифр, кроме цифр, является ложной практикой, которая может приводить к странным, нелокальным ошибкам, которые могут появиться в любой строке, обрабатывающей эти данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...