Как обрабатывать возвращаемые значения функций в Python

Функция multiply_by_ten принимает числовой аргумент, умножает его на десять и возвращает результат обратно. Прежде чем эта функция выполняет свое умножение, она проверяет, является ли argument числовым. Если argument не является числовым, функция выводит сообщение, уведомляющее, что аргумент не является цифрой и возвращает None.

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

 def multiply_by_ten(arg):
    if not str(arg).isdigit():
        print 'arg is not a digit'
        return
    return float(arg) * 10


result = multiply_by_ten('abc')

Ответ 1

У меня есть ряд проблем с этой функцией, как написано.

  • Он выполняет две очень разные вещи: он либо выполняет некоторую математику, либо возвращает полезный ответ, либо печатает сообщение. Это уже несколько красных флажков.

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

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

  • Если что-то пойдет не так, вызывающий не знает что. Он получает None назад, но на самом деле это не объясняет проблему; объяснение идет к человеку, который ничего не может с этим поделать.

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

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

  • Нет причин для того, чтобы это была функция в первую очередь! Проверка ошибок - это дублированное усилие, уже предоставленное float(), а умножение на десять - не такая обычная операция, которую необходимо учесть. Фактически это делает код вызова более длинным.

Итак, я бы сбросил функцию и просто написал это:

result = float('abc') * 10

Бонус: любой программист на Python распознает float, знайте, что он может поднять ValueError и знать, если необходимо при необходимости добавить try/except.

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

Ответ 2

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

 def multiply_by_ten(arg):
    return float(arg) * 10


try:
    result = multiply_by_ten('abc')
except ValueError:
    pass

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

>>> "0.01e-6".isdigit()
False
>>> float("0.01e-6")
1e-8

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

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

Наконец, небольшая перспектива. На С++, статически типизированном языке, существует, вероятно, тысяча различных реализаций любого, необязательного, объединения и варианта, все, что предназначено для хранения нескольких типов в одном контейнере. Даже в статически типизированных языках полиморфизм полезен. Если используется щадящий, полиморфизм является полезной чертой языка.

Ответ 3

Общей практикой обработки ошибки в Python является не возвращение значения, отличного от ожидаемого типа значения, а путем создания исключения.

Как указано @AGN Gazer, вы пойдете еще быстрее, поймав исключение ошибочного броска, избегая функции для простого умножения:

try:
   result = float('abc') * 10
except:
   print('Error: not a valid number')

Если мы останемся на вашей старой структуре кода, а не:

def multiply_by_ten(arg):
   if not str(arg).isdigit():
       print 'arg is not a digit'
       return
   return float(arg) * 10

Вам следует:

def multiply_by_ten(arg):
   if not str(arg).isdigit():
       raise Exception('NotNumber', 'The argument is not a number')
   # Do some other stuff? Otherwise not really helpful
   return float(arg) * 10

И в вашем вызове:

try:
   result = multiply_by_ten('abc')
catch Exception as e:
   if e.args[0] == 'NotNumber':
      print('Error: '+e.args[1])
   else:
      raise

Ответ 4

Если вы всегда ожидаете, число повысит значение exception, это означает, что возникла проблема.

Если значение может отсутствовать, и оно не конфликтует с возвратом логики программы None.

И самое главное последовательность кода - что вы делаете в других местах вашего кода?

Ответ 5

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

В этом случае, поскольку вы возвращаете числовое значение, не используя None ИЛИ строку, указывающую на возникновение проблемы, вы можете ограничить попытку вернуть некоторое число, чтобы указать, что возникла проблема: некоторые функции возвращать числа, как -1, как сигналы о возникшей проблеме. Но это не сработает в этом случае, если вход был -0.1.

Если, как вы говорите, вы действительно хотите вернуть только один тип значения (всегда ints OR всегда плавает), то я не вижу много параметров, кроме проверки заранее.

Параметры могут включать использование оператора try/except, как предлагают другие авторы.

Ответ 6

Это глупо. Функция всегда возвращает ровно один тип. Если функция может возвращать либо int, либо None, тогда это тип суммы, в частности Optional[int].