Чи слід завжди вказувати тип виключення у операторах "крім"?


93

При використанні PyCharm IDE використання except:без винятку типу запускає нагадування від IDE про те, що це положення про виключення Too broad.

Чи слід ігнорувати цю пораду? Або це Pythonic завжди конкретний тип виключення?


Якщо ви хочете дізнатися про це більше, ніж відповіді, google ковтає винятки. Поруч з ними можна розміщувати всілякі цікаві доси та донти. Код пахне - ще один.
Тоні Хопкінсон

Відповіді:


89

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

Наприклад, якщо ви вставляєте рядок у базу даних, ви можете захопити виняток, який вказує на те, що рядок вже існує, тому ви можете зробити оновлення.

try:
    insert(connection, data)
except:
    update(connection, data)

Якщо ви вказали голий except:, ви також побачите помилку сокета, що вказує на те, що сервер бази даних перейшов. Найкраще виловлювати лише винятки, з якими ви знаєте, як впоратися - програмі часто краще відмовитись від точки винятку, ніж продовжувати, але вести себе дивними несподіваними способами.

Один випадок, коли ви, можливо, захочете використовувати голий, except:знаходиться на найвищому рівні програми, яку потрібно завжди виконувати, як, наприклад, мережевий сервер. Але тоді вам потрібно бути дуже обережним, щоб реєструвати винятки, інакше буде неможливо розібратися, що відбувається не так. В основному, у програмі, яка це робить, має бути не менше одного місця.

Наслідком всього цього є те, що ваш код ніколи не повинен робити, raise Exception('some message')оскільки він змушує клієнтський код використовувати except:(або except Exception:це майже так само погано). Ви повинні визначити виняток, характерний для проблеми, про яку ви хочете подати сигнал (можливо, успадковуючи якийсь вбудований підклас винятків, як ValueErrorабо TypeError). Або слід підняти конкретний вбудований виняток. Це дає змогу користувачам вашого коду бути обережними у пошуку винятків, з якими вони хочуть працювати.


7
+1 Дуже вірно. Ще цікавіше з прикладу: except:також ловить (серед іншого) NameErrorі AttributeError, якщо ви неправильно написали щось у tryблоці (наприклад, ваша функція "вставки" насправді викликається insert_oneтому, що хтось не цінував послідовність стільки, скільки слід), це завжди мовчки намагається update().

1
То що робити, коли вам потрібно переконатися, що виняток не перекидається над поточним сайтом викликів? Тобто - я потрапив на всі конкретні винятки, які я можу передбачити, що їх кинуть, тепер мені потрібно додати, що "якщо ця річ, про яку я не думав, буде кинуто, мені потрібно записати її, перш ніж вона вбиває запущений контекст виконання" (наприклад, main())?
Адам Паркін

Про таку ситуацію я говорю в абзаці, що починається «Один випадок ...». Це, безумовно, потрібно іноді, але будь-яка програма повинна дійсно мати лише одне місце, яке це робить. І вам потрібно бути обережними, щоб в журналі було зрозуміло, що відбувається, інакше вам доведеться розчарувати час, намагаючись розібратися, чому ваша програма не поводиться з подією / запитом / як би не було правильно.
babbageclunk

3
@delnan: Гірше за це. except Exception:зловить NameErrorі AttributeErrorтеж. Те, що робить голою except:настільки погано, це те, що вона ловить речі, які не мають спійманого бізнесу, наприклад SystemExit(піднятий під час дзвінка exitабо sys.exit, і тепер ви перешкодили наміченому виходу) та KeyboardInterrupt(знову ж таки, якщо користувач вдарив Ctrl-C, ви, ймовірно, не хочете продовжувати бігати лише для того, щоб насупити їх). Тільки останній має будь-який реальний сенс ловити, і його слід чітко ловити. Принаймні, except Exception:дозвольте цим двом нормально поширюватися.
ShadowRanger

38

Ви не повинні ігнорувати поради, які дає вам перекладач.

З посібника зі стилю PEP-8 для Python:

Уловлюючи винятки, зазначайте конкретні винятки, коли це можливо, замість використання голого, за винятком: пункту.

Наприклад, використовуйте:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

Оголене, за винятком: пункт вилучить виключення SystemExit і KeyboardInterrupt, що ускладнить переривання програми з Control-C і може замаскувати інші проблеми. Якщо ви хочете зафіксувати всі винятки, які посилаються на помилки програмної програми, використовуйте крім винятку: (оголене, за винятком випадків, еквівалентне, крім BaseException :).

Хорошим правилом є обмеження використання голих "за винятком" пунктів на два випадки:

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



9

Це не специфічно для Python.

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

Таким чином, ви зберігаєте код, який у надзвичайних обставинах може викликати проблему та вирішення "поруч".

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

Якщо ви поставите спробу лову навколо цього, то незалежно від того, яка проблема була у вашому розпорядженні файлів (лише читання, дозволи, UAC, насправді не pdf тощо), кожен потрапить до вашого файлу не знайденого улову та вашого користувача кричить "але це там, цей код - лайно"

Зараз є декілька ситуацій, коли ви можете зловити все, але вибирати їх слід свідомо.

Вони вловлюють, скасовують деякі локальні дії (наприклад, створення або блокування ресурсу (відкриття файлу на диску для запису, наприклад), потім ви кидаєте виняток знову, щоб вирішуватись на більш високому рівні)

Інший, який ти - тобі не байдуже, чому пішло не так Наприклад, друк. Можливо, у вас є улов у всьому світі, щоб сказати, що з принтером є якась проблема, будь ласка, розіб'йтесь, а не вбивайте програму через неї. Она подібно марно, якщо ваш код виконав ряд окремих завдань за допомогою якогось розкладу, ви не хочете, щоб вся справа загинула, оскільки одна з задач не вдалася.

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


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

2
+1 за "вам байдуже, чому це пішло не так". Я використовую його в декількох місцях навколо одного рядка коду, де я аналізую дату / час з URL-адреси. Бібліотека розбору дати / часу третьої сторони не містить усіх винятків, які вона може викинути (я знайшов OverflowError та TypeError на додаток до стандартного ValueError, але їх, мабуть, більше), і все одно мені дійсно все одно, чому було викинуто виняток, я просто хочу повернути користувачеві повідомлення про помилку, у якому сказано, що з датою / часом щось не так.
Майкл Родбі

3

Ви також будете ловити, наприклад, Control-C з цим, тому не робіть цього, якщо ви не кинете його ще раз. Однак у такому випадку вам краще скористатися "нарешті".


3

Завжди вказуйте тип винятку, існує багато типів ви не хочете зловити, як SyntaxError, KeyboardInterrupt, і MemoryErrorт.д.


2
except Exception:вдалося б уникнути перерахованих вище типів, які ми не хочемо ловити?
HorseloverFat

except Exceptionдобре.
Ульріх Екхардт

4
@HorseloverFat: except Exceptionловить SyntaxErrorі MemoryErrorтому, що це їх базовий клас. KeyboardInterrupt, SystemExit(підняв sys.exit()) не спійманий (вони є негайними підкласами BaseException)
jfs

здається, що це не ідеально тоді - краще уточнити більш точно.
HorseloverFat

3

Ось місця, де я користуюся, окрім без типу

  1. швидке та брудне прототипування

Це головне використання мого коду для неперевірених винятків

  1. функція верхнього рівня main (), де я реєструю кожне невиконане виняток

Я завжди додаю це, щоб виробничий код не розсипав стеки

  1. між шарами програми

У мене є два способи зробити це:

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

Я вважаю за краще це таким чином, мені легше виявити, які винятки мали бути належним чином виловлені: я краще "бачу" проблему, коли виняток нижчого рівня реєструється вищим рівнем

  • Другий спосіб зробити це: у всіх функціях верхнього рівня шарів нижнього рівня вбудований код у загальний, за винятком випадків, коли він фіксує всі необроблені винятки на цьому конкретному шарі.

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


-14

Спробуйте це:

try:
    #code
except ValueError:
    pass

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

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