Відповіді:
Як я вручну кидаю / піднімаю виняток у Python?
Використовуйте найбільш конкретний конструктор винятків, який семантично відповідає вашій проблемі .
Будьте конкретні у своєму повідомленні, наприклад:
raise ValueError('A very specific bad thing happened.')
Уникайте підняття родового Exception
. Щоб зловити його, вам доведеться спіймати всі інші більш конкретні винятки, які його підкласують.
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
Наприклад:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
І більш конкретний улов не буде загальним винятком:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
заяваraise ValueError('A very specific bad thing happened')
що також зручно дозволяє дозволити конструктору довільну кількість аргументів:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
До цих аргументів звертається args
атрибут на Exception
об'єкті. Наприклад:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
відбитки
('message', 'foo', 'bar', 'baz')
У Python 2.5 message
було додано фактичний атрибут на BaseException
користь заохочення користувачів до підкласу Винятки та припинення використання args
, але введення message
та початкове припинення аргументів було відмінено .
except
пунктЯкщо всередині окрім пункту, окрім пункту, ви можете, наприклад, записати, що трапився певний тип помилки, а потім повторно підвищити. Найкращий спосіб зробити це при збереженні сліду стека - скористатися операцією підйому. Наприклад:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Ви можете зберегти стек-трек (і значення помилки) за допомогою sys.exc_info()
, але це набагато більше схильний до помилок і має проблеми сумісності між Python 2 і 3 , вважайте за краще використовувати голий raise
для повторного підйому.
Для пояснення - sys.exc_info()
повертає тип, значення та прослідкування.
type, value, traceback = sys.exc_info()
Це синтаксис в Python 2 - зауважте, що це не сумісно з Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Якщо ви хочете, ви можете змінити те, що відбувається з вашим новим рейсом - наприклад, встановити новий args
для примірника:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
І ми зберегли цілу прослідку, змінюючи аргументи. Зауважте, що це не найкраща практика, і це недійсний синтаксис в Python 3 (що робить сумісність набагато складнішою для обходу).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
У Python 3 :
raise error.with_traceback(sys.exc_info()[2])
Знову ж таки: уникайте вручну маніпулювати слідами. Він менш ефективний і більше схильний до помилок. І якщо ви використовуєте нитку, і sys.exc_info
ви навіть можете отримати неправильний прослідкування (особливо якщо ви використовуєте обробку виключень для потоку управління - чого я особисто схильний уникати.)
У Python 3 ви можете ланцюжок винятків, які зберігають сліди:
raise RuntimeError('specific message') from error
Бережись:
Вони легко ховаються і навіть потрапляють у виробничий код. Ви хочете підняти виняток, і виконуючи їх, викличете виняток, але не той, який призначений!
Дійсно в Python 2, але не в Python 3 є наступним:
raise ValueError, 'message' # Don't do this, it's deprecated!
Дійсний лише у значно старших версіях Python (2.4 і новіших), ви все ще можете бачити людей, що піднімають рядки:
raise 'message' # really really wrong. don't do this.
У всіх сучасних версіях це фактично підвищить значення a TypeError
, оскільки ви не піднімаєте BaseException
тип. Якщо ви не перевіряєте правильність винятку і не маєте рецензента, який обізнаний з проблемою, він може почати виробництво.
Я піднімаю винятки, щоб попередити споживачів мого API, якщо вони його неправильно використовують:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
"Я хочу зробити помилку навмисно, щоб вона перейшла в виняток"
Ви можете створити власні типи помилок, якщо ви хочете вказати, що з вашою програмою щось конкретно не так, просто підкласуйте відповідну точку в ієрархії винятків:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
та використання:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
здається, роблю те, що хочу, і я ніколи не стикався з проблемами. Але це відчуває хакі, і це не прийнята практика. Чи є кращий спосіб?
Exception
батьківського класу - ви можете підкласирувати щось більш конкретне, і слід робити це, якщо це має сенс.
AppError
виняток. Можливо, краще використовувати вбудовану помилку на кшталтAttributeError
НЕ робіть цього . Підняти оголене
Exception
- це зовсім не правильно; см відмінний відповідь Аарон Холла замість цього.
Не можна отримати набагато пітонічніше, ніж це:
raise Exception("I know python!")
Перегляньте документи операторів підвищення для python, якщо вам потрібна додаткова інформація.
У Python3 є 4 різні синтаксиси для ранжування винятків:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. підвищити виняток проти 2. підвищити виняток (args)
Якщо ви використовуєте raise exception (args)
для підняття винятку, тоді args
друк буде надруковано під час друку об'єкта виключення - як показано в прикладі нижче.
#raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
#raise execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3. підняти
raise
заява без жодних аргументів повторює останній виняток. Це корисно, якщо вам потрібно виконати деякі дії після того, як виберете виняток, а потім хочете його повторно підвищити. Але якщо раніше не було винятку, raise
заява піднімає TypeError
Виняток.
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: #Output ->
somefunction() #some cleaning
raise #Traceback (most recent call last):
#File "python", line 8, in <module>
#ZeroDivisionError: division by zero
4. підняти виняток (args) з original_exception
Це твердження використовується для створення ланцюга винятків, в якому виняток, який піднімається у відповідь на інший виняток, може містити деталі вихідного винятку - як показано в прикладі нижче.
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
Вихід:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
exception(args)
більшexception (args)
raise exception(args) from None
слід сказати, що наразі активний виняток оброблявся і вже не представляє інтересу. В іншому випадку, якщо ви підвищите виняток всередині except
блоку, і він не обробляється, відстеження обох винятків буде показано розділеним повідомленням "Під час обробки вищевказаного винятку стався інший виняток"
Для загального випадку, коли вам потрібно кинути виняток у відповідь на деякі несподівані умови, і ви ніколи не збираєтеся ловити, а просто невдало швидко, щоб ви могли налагодити звідти, якщо це коли-небудь трапиться, - здається, найбільш логічним AssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
ValueError
ніж AssertionError
тому, що немає жодних проблем із твердженням (оскільки жодне тут не робиться) - проблема полягає у значенні. Якщо ви дійсно хочете AssertionError
в цьому випадку, пишіть assert distance > 0, 'Distance must be positive'
. Але ви не повинні помилитися перевірити таким чином, оскільки твердження можна вимкнути ( python -O
).
-O
.
Прочитайте спочатку наявні відповіді, це лише додаток.
Зауважте, що ви можете створювати винятки за допомогою аргументів або без них.
Приклад:
raise SystemExit
виходить із програми, але ви, можливо, захочете дізнатися, що сталося. Отже, ви можете використовувати це.
raise SystemExit("program exited")
це надрукує "програму, що вийшла" на stderr перед закриттям програми.
raise SystemExit()
був би кращий вибір? Чому перший навіть працює?
Ще один спосіб кинути винятки - це assert
. Ви можете скористатись assrt, щоб перевірити, чи виконується умова, якщо ні, то вона підвищиться AssertionError
. Докладніше дивіться тут .
def avg(marks):
assert len(marks) != 0,"List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))
mark1 = []
print("Average of mark1:",avg(mark1))
Зазначимо: бувають випадки, коли ви хочете обробляти загальні винятки. Якщо ви обробляєте купу файлів і реєструєте свої помилки, можливо, ви захочете виявити будь-яку помилку, яка виникає для файлу, записати його та продовжити обробку решти файлів. У цьому випадку a
try:
foo()
except Exception as e:
print(str(e)) # Print out handled error
блокувати хороший спосіб це зробити. Ви все ще хочете raise
конкретних винятків, щоб знати, що вони означають.
Ви повинні навчитися заяві про підвищення python для цього. Його слід зберігати всередині пробного блоку. Приклад -
try:
raise TypeError #remove TypeError by any other error if you want
except TypeError:
print('TypeError raised')
raise
те, що мені потрібно, щоб мати змогу виконувати власну налагодження помилок на кількох рівнях виконання коду, не порушуючи слід стека.