Збільшити виняток проти повернути Немає у функціях?


85

Яка краща практика в визначеній користувачем функції в 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. Або це погана форма - повторно викликати виняток з іншою назвою?



3
Я схиляюся до створення винятку, тому змушений обробляти виняток у функції виклику. Якщо я забув перевірити, чи на виході у функції виклику немає результату None, я міг мати приховану помилку. Якщо ви повернули None, сподіваємось, наступний рядок у функції виклику призведе до помилки AttributeError. Однак якщо повернене значення додається до словника, а потім 100 викликів функцій у іншому вихідному файлі виникає AttributeError, ви отримаєте задоволення, знаходячи, чому це значення було None.
IceArdor

Як правило, я також уникаю значень, які мають особливе значення або мають кілька підписів для однієї функції (це може повернути рядок або None).
IceArdor

Відповіді:


91

Це справді питання семантики. Що foo = latestpdf(d) означає ?

Чи цілком розумно, що немає останнього файлу? Тоді звичайно, просто поверніть None.

Ви сподіваєтесь завжди знайти найновіший файл? Поставте виняток. І так, повторне підняття більш відповідного винятку - це нормально.

Якщо це лише загальна функція, яка повинна застосовуватися до будь-якого каталогу, я б зробив першу і повернув None. Якщо каталог, наприклад, мається на увазі конкретний каталог даних, який містить відомий набір файлів програми, я б викликав виняток.


3
Ще один момент, який слід врахувати: якщо виникає виняток, можна вкласти повідомлення, але ми не можемо зробити це при поверненні None.
kawing-chiu

9

Я б запропонував кілька пропозицій, перш ніж відповісти на ваше запитання, оскільки це може відповісти вам.

  • Завжди називайте свої функції описовими. latestpdfозначає дуже мало для когось, але, переглядаючи вашу функцію, ви latestpdf()отримуєте останню версію PDF. Я б запропонував назвати це getLatestPdfFromFolder(folder).

Як тільки я це зробив, стало зрозуміло, що воно повинно повернути .. Якщо PDF-файлу немає, викликайте виняток. Але почекайте там більше ..

  • Дотримуйтесь чітко визначених функцій. Оскільки не очевидно, що повинен робити somefuc, і (мабуть) не очевидно, як це пов’язано з отриманням останнього pdf, я б запропонував вам перемістити його. Це робить код набагато читабельнішим.

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

Сподіваюся, це допомагає!


Або get_latest_pdf_from_folder. Дійсно, Pep8: "Назви функцій мають бути малими, а слова, розділені підкресленнями, якщо це необхідно для поліпшення читабельності".
PatrickT

7

Зазвичай я вважаю за краще обробляти винятки внутрішньо (тобто спробувати / за винятком всередині викликаної функції, можливо, повертаючи None), оскільки python динамічно набирається. Загалом, я вважаю це викликом судження так чи інакше, але в мові, що динамічно набирається, є невеликі фактори, які нахиляють терези на користь не передачі винятку абоненту:

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

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

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

що, дев'ять разів із десяти, те, що абонент побачить, якщо ви повернете не оброблений Ні, не турбуйтеся.

(Все це змушує мене бажати, щоб винятки python мали causeатрибути за замовчуванням, як у java, що дозволяє передавати винятки в нові винятки, щоб ви могли відновити все, що хочете, і ніколи не втратити першоджерело проблеми.)


Аргумент про те, що можливі винятки не визначені і тому їх важко вловити, є дуже вагомим аргументом для Python.
snorberhuis

4

з набором python 3.5 :

приклад функції при поверненні None буде:

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

і під час винятку буде:

def latestpdf(folder: str) -> str 

варіант 2 здається більш читабельним і пітонічним

(+ можливість додати коментар до винятку, як зазначено раніше.)


4
Union[str, None]повинно бутиOptional[str]
Георгій

2
скорочення, але ти маєш рацію, це читабельніше. не редагуючи, тому обидва варіанти тут.
Асаф

2 є потенційно більш читабельним, але (на жаль?) Підказки типу не означають, що може бути створено виняток. Нещодавно я виявив, що 1 допоможе виявити більше помилок, оскільки ви змушені обробляти повернення None.
jonespm

2

Загалом, я б сказав, що слід зробити виняток, якщо сталося щось катастрофічне, що неможливо відновити (тобто ваша функція має справу з яким-небудь Інтернет-ресурсом, до якого неможливо підключитися), і ви повинні повернути None, якщо ваша функція дійсно повинна щось повернути але нічого не було б доречним для повернення (тобто "None", якщо ваша функція намагається зрівняти підрядок у рядку, наприклад).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.