Використання Python "підняти з"


197

Яка різниця між raiseі raise fromв Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

яка врожайність

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

і

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

яка врожайність

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

9
Ви читали PEP-3134 ?
jonrsharpe

4
Тепер використовуйте raise IndexError from None, скажімо.
Martijn Pieters

11
Хе. raise IndexError from Falseпіднімає a TypeError, а не an IndexError. Зробив мій день.
Божевільний фізик


Не впевнений, чи це правильне місце, щоб згадати про це, але для тих, хто використовує Spyder: Весь цей конструкт там не працює. Це питання вже більше 3-х років ( github.com/spyder-ide/spyder/isissue/2943 ), але, здається, вони вважають, що немає необхідності в ланцюгових винятках.
Еміль Боде

Відповіді:


230

Різниця полягає в тому, що при використанні from, то __cause__атрибут встановлений , і повідомлення заявляє , що виключення було безпосередньо викликано . Якщо ви не увімкнете, fromтоді не __cause__встановлено "ні", але може бути встановлено також __context__атрибут , а відслідковування назад показує контекст, як під час обробки чогось іншого трапилося .

Налаштування __context__відбувається, якщо ви використовували raiseобробник винятків; якщо ви використовували raiseінше місце, також не __context__встановлено.

Якщо __cause__встановлено a, __suppress_context__ = Trueна винятку також встановлюється прапор; коли __suppress_context__встановлено значення True, __context__ігнорується при друкуванні сліду.

Піднімаючи з обробника винятків, де ви не хочете показувати контекст (не хочу, щоб під час обробки іншого повідомлення про виняток трапилося ), тоді використовуйте raise ... from Noneдля встановлення __suppress_context__на True.

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

Дивіться документацію raiseтвердження :

fromРозділ використовується для виключення зчеплення: якщо дано, друге вираз має бути іншим клас виключення або екземпляр, який потім буде приєднаний до підвищеного виключенню в якості __cause__атрибута (який доступний для запису). Якщо піднятий виняток не обробляється, обидва винятки будуть надруковані:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Подібний механізм працює неявно, якщо виняток піднімається всередині обробника винятків або finallyпункту: попередній виняток потім додається як __context__атрибут нового винятку :

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Також дивіться документацію про вбудовані винятки, щоб отримати детальну інформацію про контекст та викликати інформацію, що додається до винятків.


11
Чи є якась - або причина явно ланцюг винятків , використовуючи fromі __cause__замість неявнога __context__? Чи є випадки, коли можна було б застосувати інший виняток, ніж той, що спійманий except?
darkfeline

13
@darkfeline: Скажімо, API вашої бази даних підтримує відкриття баз даних з різних джерел, включаючи Інтернет та диск. Ваш API завжди підвищить, DatabaseErrorякщо не вдасться відкрити базу даних. Але якщо збій є результатом того, IOErrorщо файл не вдалося відкрити, або HTTPErrorтому, що URL-адреса не працювала, то це контекст, який ви хочете явно включити, тож розробник за допомогою API може налагодити, чому це так. У той момент ви використовуєте raise DatabaseError from original_exception.
Martijn Pieters

4
@darkfeline: Якщо цей розробник завершує використання API баз даних у власному API та хотів би передати це IOErrorчи HTTPErrorдалі своїм споживачам, то їм доведеться це використовувати raise NewException from databaseexception.__cause__, використовуючи тепер інший виняток від того, DatabaseExceptionщо вони щойно спіймали.
Martijn Pieters

2
@ dan3: ні, немає. Ланцюжок винятків - суто функція Python 3.
Martijn Pieters

5
@ laike9m: ти маєш на увазі, коли ти обробляєш виняток foo, і хочеш створити новий виняток bar? Тоді ви можете використовувати raise bar from fooта мати стан Python, що foo безпосередньо спричинилоbar . Якщо ви НЕ використовуєте from foo, то Python буде по- , як і раніше друкувати як, але стверджують , що під час звернення foo, barбуло піднято , інше повідомлення, призначене для прапора можливу помилку в обробці помилок.
Martijn Pieters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.