Поднять исключение против возврата Нет в функциях?

87

Какая лучшая практика в пользовательской функции в Python: raiseисключение или return None? Например, у меня есть функция, которая находит самый последний файл в папке.

def latestpdf(folder):
    # list the files and sort them
    try:
        latest = files[-1]
    except IndexError:
        # Folder is empty.
        return None  # One possibility
        raise FileNotFoundError()  # Alternative
    else:
        return somefunc(latest)  # In my case, somefunc parses the filename

Другой вариант - оставить исключение и обработать его в коде вызывающего абонента, но я считаю, что проще иметь дело с a, FileNotFoundErrorчем с файлом IndexError. Или это дурной тон - повторно вызывать исключение с другим именем?

паркидарк
источник
Аналогично: stackoverflow.com/questions/1152541/…
codeape
3
Я склоняюсь к возникновению исключения, поэтому я вынужден обрабатывать исключение в вызывающей функции. Если я забуду проверить, нет ли на выходе вызывающей функции, у меня может быть скрытая ошибка. Если вы вернули None, мы надеемся, что следующая строка в вызывающей функции вызовет AttributeError. Однако, если возвращаемое значение добавляется в словарь, а затем 100 вызовов функций в другом исходном файле возникает AttributeError, вам будет интересно узнать, почему это значение было None.
IceArdor
В общем, я также избегаю значений, имеющих особое значение или имеющих несколько подписей для одной функции (она может возвращать строку или None).
IceArdor

Ответы:

92

Это действительно вопрос семантики. Что foo = latestpdf(d) значит ?

Совершенно разумно, что нет последнего файла? Тогда конечно, просто верните None.

Вы ожидаете, что всегда найдете последний файл? Поднимите исключение. И да, можно повторно поднять более подходящее исключение.

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

Иви
источник
3
Еще один момент, который следует учитывать: если возникает исключение, сообщение может быть прикреплено, но мы не можем этого сделать при возврате None.
kawing-
10

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

  • Всегда называйте свои функции описательными. latestpdfмало что значит для кого-то, но просмотр вашей функции latestpdf()получает последний PDF-файл. Я предлагаю вам назвать это getLatestPdfFromFolder(folder).

Как только я это сделал, стало ясно, что он должен возвращать .. Если нет pdf - возбуждает исключение. Но подожди еще ..

  • Четко определите функции. Поскольку неясно, что должен делать somefuc, и (очевидно) не очевидно, как это связано с получением последней версии pdf, я предлагаю вам убрать его. Это делает код более читабельным.

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

Надеюсь это поможет!

rh0dium
источник
1
Или get_latest_pdf_from_folder. Действительно, Pep8: «Имена функций должны быть в нижнем регистре, а слова должны быть разделены подчеркиванием по мере необходимости для улучшения читаемости».
PatrickT
7

Я обычно предпочитаю обрабатывать исключения внутренне (например, try / except внутри вызываемой функции, возможно, возвращая None), потому что python динамически типизирован. В общем, я так или иначе считаю это суждением, но в динамически типизированном языке есть небольшие факторы, которые склоняют чашу весов в пользу отказа от передачи исключения вызывающей стороне:

  1. Любой, кто вызывает вашу функцию, не уведомляется об исключениях, которые могут возникнуть. Это становится своего рода искусством - знать, за какими исключениями вы охотитесь (а общих, за исключением блоков, следует избегать).
  2. if val is Noneнемного проще, чем except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Серьезно, я ненавижу не забывать вводить from django.core.exceptions import ObjectDoesNotExistтекст в верхней части всех моих файлов django, чтобы справиться с действительно распространенным вариантом использования. В мире со статической типизацией позвольте редактору сделать это за вас.

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

AttributeError: 'NoneType' object has no attribute 'foo'

что в девяти случаях из десяти - это то, что абонент увидит, если вы вернете необработанное None, не беспокойтесь.

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

Дэвид Бергер
источник
Аргумент о том, что возможные исключения не определены и поэтому их трудно уловить, является очень веским аргументом для Python.
snorberhuis
4

с Python 3.5 в типизации :

Пример функции при возврате None будет:

def latestpdf(folder: str) -> Union[str, None]

и при возникновении исключения будет:

def latestpdf(folder: str) -> str 

вариант 2 кажется более читаемым и питоническим

(+ возможность добавить комментарий к исключению, как указано ранее.)

Асаф
источник
5
Union[str, None]должно бытьOptional[str]
Георгий
2
стенография, но вы правы, она более читабельна. не редактировать, поэтому оба варианта здесь.
Asaf
1
2 потенциально более читабелен, но (к сожалению?) Подсказки типа не указывают на то, что может быть создано исключение. Недавно я обнаружил, что 1 поможет отловить больше ошибок, так как вы вынуждены обрабатывать возврат None.
jonespm
2

В общем, я бы сказал, что должно быть выбрано исключение, если произошло что-то катастрофическое, что не может быть восстановлено (т.е. ваша функция имеет дело с каким-то интернет-ресурсом, к которому невозможно подключиться), и вы должны вернуть None, если ваша функция действительно должна что-то возвращать. но ничего не следует возвращать (например, «Нет», если ваша функция пытается сопоставить подстроку в строке).


источник