функция должна возвращать результат или сообщить, что не может вычислит. Как сделать красиво и правильно?

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

вопрос не конкретный - интересует общий подход к реализации:

Некая функция вычисляет нечто, и есть два результата вычисления -

  1. Вычисление выполнено - вернуть результат.
  2. Результат вычисления не подходит - тогда результат не интересен, но нужно передать сообщение выше по иерархии вызовов.

Я придумал пока только три варианта реализации- ни один мне не нравится:

  1. в случае неудачи вернуть False или None и уже при вызове функции разобраться с выдачей сообщения.

     def MyFunction():
         # ...
         result = someCalculation()
         # ...
         if result == SOMTHING_UNFIT:
             return False
         else:
             return result
     # ...
     result = MyFunction()
     if result == False:
         print 'Вычисление закончено. Результат не получен.'
     else:
         DoSomething(result)
    
  2. в случае неудачи выбросить исключение и поймать его, там где нужно

     def MyFunction():
         # ...
         result = someCalculation()
         # ...
         if result == SOMTHING_UNFIT:
             raise MyException('Вычисление закончено. Результат не получен.')
         else:
             return result
     # ...
     try:
         result = MyFunction()
     except MyException as e:
         print(e.args)
    
     DoSomething(result)
    
  3. Вернуть словарь, с несолькоми полями

     def MyFunction():
         # ...
         result = someCalculation()
         # ...
         if result == SOMTHING_UNFIT:
             return {
                 'fit': False,
                 'message': 'Вычисление закончено. Результат не получен.',
                 'result': None,
             }
         else:
             return {
                 'fit': True,
                 'message': None,
                 'result': result,
             }
     # ...
     result_dict = MyFunction()
    
     if result_dict['fit'] == False
         print(result_dict['message'])
     else:
         DoSomething(result_dict['result'])
    

Чем мне не нравятся эти решения:

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

Вопросы:

  1. Оптимальный вариант реализации, как это обычно делается (шаблон проектирования?)
  2. есть ли какой-нибудь механизм передать сообщение наверх напрямую (сквозь цепочки вызовов функций, циклы) - типа исключения, но без сообшений об ошибках... вроде какой-нибудь GOTO, аспект, декоратор, DI контейнер, mixin? магия?

Ответы (3 шт):

Автор решения: MaxU

По-моему вариант с возвратом None самый нормальный если вы считаете, что это не исключительная ситуация. Если это исключительная ситуация - тогда надо выбрасывать исключение (Exception).

Значение None - необычное значение, которое не должно возвращаться в нормальном случае. Кроме того, возвращаемое значение легко проверить на None:

res = func(...)

if res is None:
    ...

В зависимости от необходимого поведения, можно добавить logging и печатать предупреждения (warning).

→ Ссылка
Автор решения: Sergey

Традиционно, для решения проблемы с возвратом при ошибке, в Python используется простой приём: функция возвращает 2 значения - результат своей работы и признак наличия ошибки. Как то так:

if .... :
    # Всё хорошо
    return result, False
else:
    return None, True

А в вызывающей программе пишите:

(x, error) = MyFunction(...)
if error:
    ....
→ Ссылка
Автор решения: CrazyElf

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

→ Ссылка