Подтвердить что ты не робот

Как часто пользовательские исключения определяются в python?

При попытке устранить потенциальное состояние гонки в модуле python, который я написал для мониторинга некоторых специализированных рабочих процессов, я узнал о стиле кодирования python "проще просить, чем разрешение" (EAFP), и теперь я собираю множество пользовательских исключений с try/except block, где я использовал, если /thens.

Я новичок в python, и этот стиль EAFP имеет смысл логически и, похоже, делает мой код более надежным, но что-то об этом чувствует себя за бортом. Является ли плохая практика определять одно или несколько исключений для каждого метода?

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

Здесь примерный метод, например:

class UploadTimeoutFileMissing(Exception):
    def __init__(self, value):
        self.parameter = value
    def __str__(self):
        return repr(self.parameter)

class UploadTimeoutTooSlow(Exception):
    def __init__(self, value):
        self.parameter = value
    def __str__(self):
        return repr(self.parameter)

def check_upload(file, timeout_seconds, max_age_seconds, min_age_seconds):

    timeout = time.time() + timeout_seconds

    ## Check until file found or timeout
    while (time.time() < timeout):

        time.sleep(5)
        try:
            filetime = os.path.getmtime(file)
            filesize = os.path.getsize(file)
        except OSError:
            print "File not found %s" % file
            continue

        fileage = time.time() - filetime

        ## Make sure file isn't pre-existing
        if fileage > max_age_seconds:
            print "File too old %s" % file
            continue

        ## Make sure file isn't still uploading
        elif fileage <= min_age_seconds:
            print "File too new %s" % file
            continue

        return(filetime, filesize)

    ## Timeout
    try:
        filetime
        filesize
        raise UploadTimeoutTooSlow("File still uploading")

    except NameError:
        raise UploadTimeoutFileMissing("File not sent")
4b9b3361

Ответ 1

определить одно или несколько исключений для каждого метода

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

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

В общем случае для более крупных модулей вы определите более одного исключения. Если бы вы работали над некоторой арифметической библиотекой, и вы бы определили ZeroDivisionError и OverflowError (если они еще не были определены в python, потому что вы, конечно, можете повторно использовать их), это было бы прекрасно.

Ответ 2

Является ли плохая практика определять одно или несколько исключений для каждого метода?

Да.

Один из модулей более типичен. Это зависит, конечно, от детальной семантики. Вопрос сводится к следующему: "Что вы действительно попытаетесь поймать?"

Если вы никогда не собираетесь использовать except ThisVeryDetailedException: в своем коде, то ваше очень подробное исключение не очень полезно.

Если вы можете сделать это: except Error as e: if e.some_special_case в течение очень немногих раз имеет значение, то вы можете легко упростить одно исключение на модуль и обрабатывать ваши особые случаи как атрибуты исключения, а не различные типы исключений.

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

try: 
    something
except some_module.Error as e:
    carry on

Это дает вам хорошее соглашение об именах: module.Error. Это охватывает многочисленные грехи.


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

Ответ 3

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

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

Я стараюсь, чтобы убедиться, что встроенные исключения Python никогда не появляются. Поскольку наши разработчики будут полагаться на экосистему существующих модулей, чтобы создавать свои собственные инструменты поверх того, чтобы API позволял универсальному экранированию IOError быть контрпродуктивным, тем более, что вызывающая подпрограмма может даже не знать, что она читает файловую систему ( абстракция - прекрасная вещь). Если базовый модуль не может выразить что-то значимое в этой ошибке, необходимо выполнить большую работу.

Мой подход к решению этого вопроса заключается в создании класса исключения объекта, из которого выводятся все другие исключения объекта. Существуют подклассы этого для конкретных типов задач или конкретных хост-приложений, что позволяет мне настраивать обработку ошибок (например, исключения, поднятые в Maya, запустит пользовательский интерфейс, помогающий устранить неполадки, поскольку обычное исключение будет поднято в незаметной консоли и часто будут упущены).

В класс исключения объекта встроены все виды отчетов - исключения не отображаются пользователю, также не сообщаются внутренне. Для ряда исключений я получаю IM в любое время, когда его поднимают. Другие просто тихо сообщают в базу данных, в которой я могу запрашивать последние (ежедневные или еженедельные) отчеты. Каждая ссылка на EXTENSIVE данные, полученные из сеанса пользователя, обычно включает скриншот, трассировку стека, конфигурацию системы и многое другое. Это означает, что я могу эффективно устранять проблемы до того, как они сообщаются, - и у вас больше информации на кончиках пальцев, чем большинство пользователей, которые могут предоставить.

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

Итак, нет - я не думаю, что определение исключения или два в модуле необоснованно, но они должны быть значимыми и добавлять что-то в проект. Если вы просто обмениваете IOError на raise MyIOError("I got an IO error!"), тогда вы можете переосмыслить это.

Ответ 4

Я не думаю, что для каждого возможного сценария необходимо иметь исключительно конкретное исключение. Единственный UploadTimeoutError, вероятно, будет прекрасен, и вы можете просто настроить строку исключений - в конце концов, для чего нужны строки. Обратите внимание, что у python нет отдельного исключения для каждого возможного типа синтаксической ошибки, просто общая SyntaxError.

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

>>> class MyException(Exception): pass
... 
>>> raise MyException("oops!")
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
MyException: oops!    
>>> str(MyException("oops!"))
'oops!'