Decorator is a good approach.
from functools import wraps
import time
class retry:
def __init__(self, success=lambda r:True, times=3, delay=1, raiseexception=True, echo=True):
self.success = success
self.times = times
self.raiseexception = raiseexception
self.echo = echo
self.delay = delay
def retry(fun, *args, success=lambda r:True, times=3, delay=1, raiseexception=True, echo=True, **kwargs):
ex = Exception(f"{fun} failed.")
r = None
for i in range(times):
if i > 0:
time.sleep(delay*2**(i-1))
try:
r = fun(*args, **kwargs)
s = success(r)
except Exception as e:
s = False
ex = e
# raise e
if not s:
continue
return r
else:
if echo:
print(f"{fun} failed.", "args:", args, kwargs, "nresult: %s"%r)
if raiseexception:
raise ex
def __call__(self, fun):
@wraps(fun)
def wraper(*args, retry=0, **kwargs):
retry = retry if retry>0 else self.times
return self.__class__.retry(fun, *args,
success=self.success,
times=retry,
delay=self.delay,
raiseexception = self.raiseexception,
echo = self.echo,
**kwargs)
return wraper
some usage examples:
@retry(success=lambda x:x>3, times=4, delay=0.1)
def rf1(x=[]):
x.append(1)
print(x)
return len(x)
> rf1()
[1]
[1, 1]
[1, 1, 1]
[1, 1, 1, 1]
4
@retry(success=lambda x:x>3, times=4, delay=0.1)
def rf2(l=[], v=1):
l.append(v)
print(l)
assert len(l)>4
return len(l)
> rf2(v=2, retry=10) #overwite times=4
[2]
[2, 2]
[2, 2, 2]
[2, 2, 2, 2]
[2, 2, 2, 2, 2]
5
> retry.retry(lambda a,b:a+b, 1, 2, times=2)
3
> retry.retry(lambda a,b:a+b, 1, "2", times=2)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Put it inside a while loop and break out when you’ve got the input you expect. It’s probably best to keep all code dependant on imp
in the try
as below, or set a default value for it to prevent NameError
‘s further down.
while True:
try:
imp = int(input("Importance:nt1: Highnt2: Normalnt3: Low"))
# ... Do stuff dependant on "imp"
break # Only triggered if input is valid...
except ValueError:
print("Error: Invalid number")
EDIT: user2678074 makes the valid point that this could make debugging difficult as it could get stuck in an infinite loop.
I would make two suggestions to resolve this — firstly use a for loop with a defined number of retries. Secondly, place the above in a function, so it’s kept separate from the rest of your application logic and the error is isolated within the scope of that function:
def safeIntegerInput( num_retries = 3 ):
for attempt_no in range(num_retries):
try:
return int(input("Importance:nt1: Highnt2: Normalnt3: Low"))
except ValueError as error:
if attempt_no < (num_retries - 1):
print("Error: Invalid number")
else:
raise error
With that in place, you can have a try/except outside of the function call and it’ll only through if you go beyond the max number of retries.
Сделайте while True
внутри цикла for, поместите код try
внутри и перерыв в этом цикле while
только тогда, когда ваш код преуспеет.
for i in range(0,100):
while True:
try:
# do stuff
except SomeSpecificException:
continue
break
zneak
18 янв. 2010, в 06:57
Поделиться
Я предпочитаю ограничивать количество повторений, так что, если есть проблема с этим конкретным элементом, вы, в конце концов, перейдете к следующему:
for i in range(100):
for attempt in range(10):
try:
# do thing
except:
# perhaps reconnect, etc.
else:
break
else:
# we failed all the attempts - deal with the consequences.
xorsyst
05 окт. 2011, в 15:08
Поделиться
Пакет повторных попыток — это хороший способ повторить блок кода при ошибке.
Например:
@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
print("Randomly wait 1 to 2 seconds between retries")
goneri
18 сен. 2014, в 13:48
Поделиться
Вот решение, подобное другим, но оно приведет к возникновению исключения, если ему не удастся выполнить заданное число или повторить попытку.
tries = 3
for i in range(tries):
try:
do_the_thing()
except KeyError as e:
if i < tries - 1: # i is zero indexed
continue
else:
raise
break
TheHerk
19 янв. 2016, в 21:27
Поделиться
Более «функциональный» подход без использования этих уродливых циклов while:
def tryAgain(retries=0):
if retries > 10: return
try:
# Do stuff
except:
retries+=1
tryAgain(retries)
tryAgain()
restbeckett
28 окт. 2010, в 21:51
Поделиться
Самый ясный способ — явно установить i
. Например:
i = 0
while i < 100:
i += 1
try:
# do stuff
except MyException:
continue
Tomi Kyöstilä
18 янв. 2010, в 06:18
Поделиться
Общее решение с тайм-аутом:
import time
def onerror_retry(exception, callback, timeout=2, timedelta=.1):
end_time = time.time() + timeout
while True:
try:
yield callback()
break
except exception:
if time.time() > end_time:
raise
elif timedelta > 0:
time.sleep(timedelta)
Использование:
for retry in onerror_retry(SomeSpecificException, do_stuff):
retry()
Laurent LAPORTE
04 дек. 2014, в 19:25
Поделиться
Использование рекурсии
for i in range(100):
def do():
try:
## Network related scripts
except SpecificException as ex:
do()
do() ## invoke do() whenever required inside this loop
Joseph Thomas
31 авг. 2016, в 12:31
Поделиться
В библиотеке Python Decorator есть нечто похожее.
Пожалуйста, имейте в виду, что это не проверка исключений, а возвращаемое значение. Повторяется до тех пор, пока оформленная функция не вернет True.
Немного измененная версия должна сделать свое дело.
Michael
15 июнь 2011, в 08:41
Поделиться
Вы можете использовать пакет повторной проверки Python.
Retrying
Это написано на Python, чтобы упростить задачу добавления поведения повтора к чему угодно.
ManJan
18 окт. 2017, в 17:54
Поделиться
Использование while и счетчика:
count = 1
while count <= 3: # try 3 times
try:
# do_the_logic()
break
except SomeSpecificException as e:
# If trying 3rd time and still error??
# Just throw the error- we don't have anything to hide :)
if count == 3:
raise
count += 1
Ranju R
31 авг. 2016, в 13:24
Поделиться
Если вы хотите найти решение без вложенных циклов и использования break
при успехе, вы можете разработать быструю retriable
для любой итерации. Вот пример сетевой проблемы, с которой я часто сталкиваюсь — срок сохраненной аутентификации истекает. Использование этого будет читать так:
client = get_client()
smart_loop = retriable(list_of_values):
for value in smart_loop:
try:
client.do_something_with(value)
except ClientAuthExpired:
client = get_client()
smart_loop.retry()
continue
except NetworkTimeout:
smart_loop.retry()
continue
Mikhail
24 июль 2018, в 21:30
Поделиться
for _ in range(5):
try:
# replace this with something that may fail
raise ValueError("foo")
# replace Exception with a more specific exception
except Exception as e:
err = e
continue
# no exception, continue remainder of code
else:
break
# did not break the for loop, therefore all attempts
# raised an exception
else:
raise err
Моя версия похожа на несколько выше, но не использует отдельный во while
цикла, и вновь поднимает последнее исключение, если все повторные попытки терпят неудачу. Можно явно установить err = None
наверху, но это не является строго необходимым, поскольку он должен выполнять только последний блок else
если произошла ошибка и, следовательно, установлен err
.
n8henrie
05 фев. 2019, в 16:26
Поделиться
Я использую следующие в моих кодах,
for i in range(0, 10):
try:
#things I need to do
except ValueError:
print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
time.sleep(2)
continue
break
H S Rathore
21 нояб. 2018, в 07:52
Поделиться
Вот моя идея о том, как это исправить:
j = 19
def calc(y):
global j
try:
j = j + 8 - y
x = int(y/j) # this will eventually raise DIV/0 when j=0
print("i = ", str(y), " j = ", str(j), " x = ", str(x))
except:
j = j + 1 # when the exception happens, increment "j" and retry
calc(y)
for i in range(50):
calc(i)
Amine
24 дек. 2015, в 17:45
Поделиться
увеличивает вашу переменную цикла только тогда, когда предложение try успешно
appusajeev
18 янв. 2010, в 11:12
Поделиться
Ещё вопросы
- 0Как исчезнуть элемент при переходе?
- 1Рекурсивное суммирование элементов списка
- 1Как использовать GridView AutoGenerateDeletebutton
- 0Стиль строки в абзаце с идентификатором
- 0IOS html css Z-Index и абсолютная позиция не работают для перекрывающихся DIV
- 1Панды — являются ли определенные строки dataframeA подмножеством определенных строк dataframeB?
- 1Regex.Заменить перегрузку?
- 0jQuery при наведении курсора мыши
- 0Проблема с вложенным ng-repeat
- 1SendKeys {SUBTRACT} не работает
- 0Ошибка при использовании «использования» в php
- 1Плавное вращение камеры (look-control) в A-Frame
- 1Данные не сохраняются в переменной ModelList в цикле. «Индекс был вне диапазона»
- 1Функция Python re.sub (), преобразующая « t» в пути к файлу в символ табуляции
- 0для моего проекта C ++ я создал подпапку в eclipse, как включить ее в основную?
- 0Hibernate выберите после вставки
- 0Angularjs: элементы массива Sum для диаграмм
- 0Преобразование даты в формат .ICS (1 час)
- 0Не загружайте часть HTML, пока кнопка не нажата
- 0заставить код jquery работать для динамически добавляемого контента
- 1Почему TextView не может отображать форматированный текст, ссылающийся на ресурсы Android?
- 1Инструмент для вставки JSON в JavaDoc?
- 1Как получить доступ к user_posts в Facebook Graph API
- 0Вертикально выровнять div относительно родного брата
- 0Ошибка при использовании STXXL Autogrow
- 0Маркировка подключенного компонента на случайно разделенных данных?
- 1Identity Toolkit API ранее не использовался в проекте или он отключен
- 0Невозможно использовать цепной локатор в транспортире
- 0переопределение метода базового класса с использованием виртуального не работает
- 1Элемент xmpmeta здесь не разрешен, когда я импортирую файл SVG
- 0Сделайте фон поля поиска кликабельным
- 0CSS Toggle Anchor от URL
- 1Как распечатать (или предупредить) значение из магазина ExtJS?
- 0Получение списка имен файлов, уже загруженных в DOM?
- 0Создание редактируемого выпадающего списка
- 0Таймер в AngularJS
- 0angularjs ui-router что-то в коде
- 0Как мне сделать конструктор, который принимает любой примитивный тип и преобразует в int в c ++?
- 0граница не расширяется — css
- 0манипулирование таблицей html с использованием foreach в codeigniter
- 0текстовый текст getSuggestions в диалоге jQuery
- 0Можем ли мы использовать Qt (64) для создания приложения, которое будет работать как на 32-битных, так и на 64-битных окнах?
- 0Изображения появляются в Chrome, но не в IE
- 0Проверка формы jQuery — добавление новых ключевых слов на основе значения меню «Выбрать»
- 1Как исправить ошибку json.JSONException: индекс 1 вне диапазона [0..1)
- 1Закрепление WinForm внутри другого WinForm
- 0Отображение man-страницы в C ++
- 0PHP отображает имена элементов массива
- 1Интернет перед WCF Security лучший вариант
- 1Сохранить порядок данных после слияния
Toggle table of contents sidebar
try/except#
Если вы повторяли примеры, которые использовались ранее, то наверняка
были ситуации, когда выскакивала ошибка. Скорее всего, это была ошибка
синтаксиса, когда не хватало, например, двоеточия.
Как правило, Python довольно понятно реагирует на подобные ошибки, и их
можно исправить.
Тем не менее, даже если код синтаксически написан правильно, могут
возникать ошибки. В Python эти ошибки называются исключения (exceptions).
Примеры исключений:
In [1]: 2/0 ----------------------------------------------------- ZeroDivisionError: division by zero In [2]: 'test' + 2 ----------------------------------------------------- TypeError: must be str, not int
В данном случае возникло два исключения: ZeroDivisionError и
TypeError.
Чаще всего можно предсказать, какого рода исключения возникнут во время
исполнения программы.
Например, если программа на вход ожидает два числа, а на выходе выдает
их сумму, а пользователь ввел вместо одного из чисел строку, появится
ошибка TypeError, как в примере выше.
Python позволяет работать с исключениями. Их можно перехватывать и
выполнять определенные действия в том случае, если возникло исключение.
Примечание
Когда в программе возникает исключение, она сразу завершает работу.
Для работы с исключениями используется конструкция try/except
:
In [3]: try: ...: 2/0 ...: except ZeroDivisionError: ...: print("You can't divide by zero") ...: You can't divide by zero
Конструкция try работает таким образом:
-
сначала выполняются выражения, которые записаны в блоке try
-
если при выполнения блока try не возникло никаких исключений, блок except пропускается,
и выполняется дальнейший код -
если во время выполнения блока try в каком-то месте возникло исключение,
оставшаяся часть блока try пропускается-
если в блоке except указано исключение, которое возникло, выполняется код в блоке except
-
если исключение, которое возникло, не указано в блоке except,
выполнение программы прерывается и выдается ошибка
-
Обратите внимание, что строка Cool!
в блоке try не выводится:
In [4]: try: ...: print("Let's divide some numbers") ...: 2/0 ...: print('Cool!') ...: except ZeroDivisionError: ...: print("You can't divide by zero") ...: Let's divide some numbers You can't divide by zero
В конструкции try/except может быть много except, если нужны разные
действия в зависимости от типа ошибки.
Например, скрипт divide.py делит два числа введенных пользователем:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except ValueError: print("Пожалуйста, вводите только числа") except ZeroDivisionError: print("На ноль делить нельзя")
Примеры выполнения скрипта:
$ python divide.py Введите первое число: 3 Введите второе число: 1 Результат: 3 $ python divide.py Введите первое число: 5 Введите второе число: 0 На ноль делить нельзя $ python divide.py Введите первое число: qewr Введите второе число: 3 Пожалуйста, вводите только числа
В данном случае исключение ValueError возникает, когда пользователь
ввел строку вместо числа, во время перевода строки в число.
Исключение ZeroDivisionError возникает в случае, если второе число было
равным 0.
Если нет необходимости выводить различные сообщения на ошибки ValueError
и ZeroDivisionError, можно сделать так (файл divide_ver2.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...")
Проверка:
$ python divide_ver2.py Введите первое число: wer Введите второе число: 4 Что-то пошло не так... $ python divide_ver2.py Введите первое число: 5 Введите второе число: 0 Что-то пошло не так...
Примечание
В блоке except можно не указывать конкретное исключение или
исключения. В таком случае будут перехватываться все исключения.
Это делать не рекомендуется!
try/except/else#
В конструкции try/except есть опциональный блок else. Он выполняется в
том случае, если не было исключения.
Например, если необходимо выполнять в дальнейшем какие-то операции с
данными, которые ввел пользователь, можно записать их в блоке else (файл
divide_ver3.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...") else: print("Результат в квадрате: ", result**2)
Пример выполнения:
$ python divide_ver3.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 $ python divide_ver3.py Введите первое число: werq Введите второе число: 3 Что-то пошло не так...
try/except/finally#
Блок finally — это еще один опциональный блок в конструкции try. Он
выполняется всегда, независимо от того, было ли исключение или нет.
Сюда ставятся действия, которые надо выполнить в любом случае. Например,
это может быть закрытие файла.
Файл divide_ver4.py с блоком finally:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...") else: print("Результат в квадрате: ", result**2) finally: print("Вот и сказочке конец, а кто слушал - молодец.")
Проверка:
$ python divide_ver4.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: qwerewr Введите второе число: 3 Что-то пошло не так... Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: 4 Введите второе число: 0 Что-то пошло не так... Вот и сказочке конец, а кто слушал - молодец.
Когда использовать исключения#
Как правило, один и тот же код можно написать и с использованием
исключений, и без них.
Например, этот вариант кода:
while True: a = input("Введите число: ") b = input("Введите второе число: ") try: result = int(a)/int(b) except ValueError: print("Поддерживаются только числа") except ZeroDivisionError: print("На ноль делить нельзя") else: print(result) break
Можно переписать таким образом без try/except (файл
try_except_divide.py):
while True: a = input("Введите число: ") b = input("Введите второе число: ") if a.isdigit() and b.isdigit(): if int(b) == 0: print("На ноль делить нельзя") else: print(int(a)/int(b)) break else: print("Поддерживаются только числа")
Далеко не всегда аналогичный вариант без использования исключений
будет простым и понятным.
Важно в каждой конкретной ситуации оценивать, какой вариант кода более
понятный, компактный и универсальный — с исключениями или без.
Если вы раньше использовали какой-то другой язык программирования, есть
вероятность, что в нём использование исключений считалось плохим тоном.
В Python это не так. Чтобы немного больше разобраться с этим вопросом,
посмотрите ссылки на дополнительные материалы в конце этого раздела.
raise#
Иногда в коде надо сгенерировать исключение, это можно сделать так:
raise ValueError("При выполнении команды возникла ошибка")
Встроенные исключения#
В Python есть много встроенных исключений,
каждое из которых генерируется в
определенной ситуации.
Например, TypeError обычно генерируется когда ожидался один тип данных, а передали другой
In [1]: "a" + 3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-1-5aa8a24e3e06> in <module> ----> 1 "a" + 3 TypeError: can only concatenate str (not "int") to str
ValueError когда значение не соответствует ожидаемому:
In [2]: int("a") --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-2-d9136db7b558> in <module> ----> 1 int("a") ValueError: invalid literal for int() with base 10: 'a'
Содержание:развернуть
- Как устроен механизм исключений
- Как обрабатывать исключения в Python (try except)
-
As — сохраняет ошибку в переменную
-
Finally — выполняется всегда
-
Else — выполняется когда исключение не было вызвано
-
Несколько блоков except
-
Несколько типов исключений в одном блоке except
-
Raise — самостоятельный вызов исключений
-
Как пропустить ошибку
- Исключения в lambda функциях
- 20 типов встроенных исключений в Python
- Как создать свой тип Exception
Программа, написанная на языке Python, останавливается сразу как обнаружит ошибку. Ошибки могут быть (как минимум) двух типов:
- Синтаксические ошибки — возникают, когда написанное выражение не соответствует правилам языка (например, написана лишняя скобка);
- Исключения — возникают во время выполнения программы (например, при делении на ноль).
Синтаксические ошибки исправить просто (если вы используете IDE, он их подсветит). А вот с исключениями всё немного сложнее — не всегда при написании программы можно сказать возникнет или нет в данном месте исключение. Чтобы приложение продолжило работу при возникновении проблем, такие ошибки нужно перехватывать и обрабатывать с помощью блока try/except
.
Как устроен механизм исключений
В Python есть встроенные исключения, которые появляются после того как приложение находит ошибку. В этом случае текущий процесс временно приостанавливается и передает ошибку на уровень вверх до тех пор, пока она не будет обработано. Если ошибка не будет обработана, программа прекратит свою работу (а в консоли мы увидим Traceback с подробным описанием ошибки).
💁♂️ Пример: напишем скрипт, в котором функция ожидает число, а мы передаём сроку (это вызовет исключение «TypeError»):
def b(value):
print("-> b")
print(value + 1) # ошибка тут
def a(value):
print("-> a")
b(value)
a("10")
> -> a
> -> b
> Traceback (most recent call last):
> File "test.py", line 11, in <module>
> a("10")
> File "test.py", line 8, in a
> b(value)
> File "test.py", line 3, in b
> print(value + 1)
> TypeError: can only concatenate str (not "int") to str
В данном примере мы запускаем файл «test.py» (через консоль). Вызывается функция «a«, внутри которой вызывается функция «b«. Все работает хорошо до сточки print(value + 1)
. Тут интерпретатор понимает, что нельзя конкатенировать строку с числом, останавливает выполнение программы и вызывает исключение «TypeError».
Далее ошибка передается по цепочке в обратном направлении: «b» → «a» → «test.py«. Так как в данном примере мы не позаботились обработать эту ошибку, вся информация по ошибке отобразится в консоли в виде Traceback.
Traceback (трассировка) — это отчёт, содержащий вызовы функций, выполненные в определенный момент. Трассировка помогает узнать, что пошло не так и в каком месте это произошло.
Traceback лучше читать снизу вверх ↑
В нашем примере Traceback
содержится следующую информацию (читаем снизу вверх):
TypeError
— тип ошибки (означает, что операция не может быть выполнена с переменной этого типа);can only concatenate str (not "int") to str
— подробное описание ошибки (конкатенировать можно только строку со строкой);- Стек вызова функций (1-я линия — место, 2-я линия — код). В нашем примере видно, что в файле «test.py» на 11-й линии был вызов функции «a» со строковым аргументом «10». Далее был вызов функции «b».
print(value + 1)
это последнее, что было выполнено — тут и произошла ошибка. most recent call last
— означает, что самый последний вызов будет отображаться последним в стеке (в нашем примере последним выполнилсяprint(value + 1)
).
В Python ошибку можно перехватить, обработать, и продолжить выполнение программы — для этого используется конструкция try ... except ...
.
Как обрабатывать исключения в Python (try except)
В Python исключения обрабатываются с помощью блоков try/except
. Для этого операция, которая может вызвать исключение, помещается внутрь блока try
. А код, который должен быть выполнен при возникновении ошибки, находится внутри except
.
Например, вот как можно обработать ошибку деления на ноль:
try:
a = 7 / 0
except:
print('Ошибка! Деление на 0')
Здесь в блоке try
находится код a = 7 / 0
— при попытке его выполнить возникнет исключение и выполнится код в блоке except
(то есть будет выведено сообщение «Ошибка! Деление на 0»). После этого программа продолжит свое выполнение.
💭 PEP 8 рекомендует, по возможности, указывать конкретный тип исключения после ключевого слова except
(чтобы перехватывать и обрабатывать конкретные исключения):
try:
a = 7 / 0
except ZeroDivisionError:
print('Ошибка! Деление на 0')
Однако если вы хотите перехватывать все исключения, которые сигнализируют об ошибках программы, используйте тип исключения Exception
:
try:
a = 7 / 0
except Exception:
print('Любая ошибка!')
As — сохраняет ошибку в переменную
Перехваченная ошибка представляет собой объект класса, унаследованного от «BaseException». С помощью ключевого слова as
можно записать этот объект в переменную, чтобы обратиться к нему внутри блока except
:
try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(e)
> [Errno 2] No such file or directory: 'ok123.txt'
В примере выше мы обращаемся к объекту класса «FileNotFoundError» (при выводе на экран через print
отобразится строка с полным описанием ошибки).
У каждого объекта есть поля, к которым можно обращаться (например если нужно логировать ошибку в собственном формате):
import datetime
now = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S")
try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(f"{now} [FileNotFoundError]: {e.strerror}, filename: {e.filename}")
> 20-11-2021 18:42:01 [FileNotFoundError]: No such file or directory, filename: ok123.txt
Finally — выполняется всегда
При обработке исключений можно после блока try
использовать блок finally
. Он похож на блок except
, но команды, написанные внутри него, выполняются обязательно. Если в блоке try
не возникнет исключения, то блок finally
выполнится так же, как и при наличии ошибки, и программа возобновит свою работу.
Обычно try/except
используется для перехвата исключений и восстановления нормальной работы приложения, а try/finally
для того, чтобы гарантировать выполнение определенных действий (например, для закрытия внешних ресурсов, таких как ранее открытые файлы).
В следующем примере откроем файл и обратимся к несуществующей строке:
file = open('ok.txt', 'r')
try:
lines = file.readlines()
print(lines[5])
finally:
file.close()
if file.closed:
print("файл закрыт!")
> файл закрыт!
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> print(lines[5])
> IndexError: list index out of range
Даже после исключения «IndexError», сработал код в секции finally
, который закрыл файл.
p.s. данный пример создан для демонстрации, в реальном проекте для работы с файлами лучше использовать менеджер контекста with.
Также можно использовать одновременно три блока try/except/finally
. В этом случае:
- в
try
— код, который может вызвать исключения; - в
except
— код, который должен выполниться при возникновении исключения; - в
finally
— код, который должен выполниться в любом случае.
def sum(a, b):
res = 0
try:
res = a + b
except TypeError:
res = int(a) + int(b)
finally:
print(f"a = {a}, b = {b}, res = {res}")
sum(1, "2")
> a = 1, b = 2, res = 3
Else — выполняется когда исключение не было вызвано
Иногда нужно выполнить определенные действия, когда код внутри блока try
не вызвал исключения. Для этого используется блок else
.
Допустим нужно вывести результат деления двух чисел и обработать исключения в случае попытки деления на ноль:
b = int(input('b = '))
c = int(input('c = '))
try:
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
else:
print(f"a = {a}")
> b = 10
> c = 1
> a = 10.0
В этом случае, если пользователь присвоит переменной «с» ноль, то появится исключение и будет выведено сообщение «‘Ошибка! Деление на 0′», а код внутри блока else
выполняться не будет. Если ошибки не будет, то на экране появятся результаты деления.
Несколько блоков except
В программе может возникнуть несколько исключений, например:
- Ошибка преобразования введенных значений к типу
float
(«ValueError»); - Деление на ноль («ZeroDivisionError»).
В Python, чтобы по-разному обрабатывать разные типы ошибок, создают несколько блоков except
:
try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
except ValueError:
print('Число введено неверно')
else:
print(f"a = {a}")
> b = 10
> c = 0
> Ошибка! Деление на 0
> b = 10
> c = питон
> Число введено неверно
Теперь для разных типов ошибок есть свой обработчик.
Несколько типов исключений в одном блоке except
Можно также обрабатывать в одном блоке except сразу несколько исключений. Для этого они записываются в круглых скобках, через запятую сразу после ключевого слова except
. Чтобы обработать сообщения «ZeroDivisionError» и «ValueError» в одном блоке записываем их следующим образом:
try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except (ZeroDivisionError, ValueError) as er:
print(er)
else:
print('a = ', a)
При этом переменной er
присваивается объект того исключения, которое было вызвано. В результате на экран выводятся сведения о конкретной ошибке.
Raise — самостоятельный вызов исключений
Исключения можно генерировать самостоятельно — для этого нужно запустить оператор raise
.
min = 100
if min > 10:
raise Exception('min must be less than 10')
> Traceback (most recent call last):
> File "test.py", line 3, in <module>
> raise Exception('min value must be less than 10')
> Exception: min must be less than 10
Перехватываются такие сообщения точно так же, как и остальные:
min = 100
try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')
> Моя ошибка
Кроме того, ошибку можно обработать в блоке except
и пробросить дальше (вверх по стеку) с помощью raise
:
min = 100
try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')
raise
> Моя ошибка
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> raise Exception('min must be less than 10')
> Exception: min must be less than 10
Как пропустить ошибку
Иногда ошибку обрабатывать не нужно. В этом случае ее можно пропустить с помощью pass
:
try:
a = 7 / 0
except ZeroDivisionError:
pass
Исключения в lambda функциях
Обрабатывать исключения внутри lambda функций нельзя (так как lambda записывается в виде одного выражения). В этом случае нужно использовать именованную функцию.
20 типов встроенных исключений в Python
Иерархия классов для встроенных исключений в Python выглядит так:
BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
ArithmeticError
AssertionError
...
...
...
ValueError
Warning
Все исключения в Python наследуются от базового BaseException
:
SystemExit
— системное исключение, вызываемое функциейsys.exit()
во время выхода из приложения;KeyboardInterrupt
— возникает при завершении программы пользователем (чаще всего при нажатии клавиш Ctrl+C);GeneratorExit
— вызывается методомclose
объектаgenerator
;Exception
— исключения, которые можно и нужно обрабатывать (предыдущие были системными и их трогать не рекомендуется).
От Exception
наследуются:
1 StopIteration
— вызывается функцией next в том случае если в итераторе закончились элементы;
2 ArithmeticError
— ошибки, возникающие при вычислении, бывают следующие типы:
FloatingPointError
— ошибки при выполнении вычислений с плавающей точкой (встречаются редко);OverflowError
— результат вычислений большой для текущего представления (не появляется при операциях с целыми числами, но может появиться в некоторых других случаях);ZeroDivisionError
— возникает при попытке деления на ноль.
3 AssertionError
— выражение, используемое в функции assert
неверно;
4 AttributeError
— у объекта отсутствует нужный атрибут;
5 BufferError
— операция, для выполнения которой требуется буфер, не выполнена;
6 EOFError
— ошибка чтения из файла;
7 ImportError
— ошибка импортирования модуля;
8 LookupError
— неверный индекс, делится на два типа:
IndexError
— индекс выходит за пределы диапазона элементов;KeyError
— индекс отсутствует (для словарей, множеств и подобных объектов);
9 MemoryError
— память переполнена;
10 NameError
— отсутствует переменная с данным именем;
11 OSError
— исключения, генерируемые операционной системой:
ChildProcessError
— ошибки, связанные с выполнением дочернего процесса;ConnectionError
— исключения связанные с подключениями (BrokenPipeError, ConnectionResetError, ConnectionRefusedError, ConnectionAbortedError);FileExistsError
— возникает при попытке создания уже существующего файла или директории;FileNotFoundError
— генерируется при попытке обращения к несуществующему файлу;InterruptedError
— возникает в том случае если системный вызов был прерван внешним сигналом;IsADirectoryError
— программа обращается к файлу, а это директория;NotADirectoryError
— приложение обращается к директории, а это файл;PermissionError
— прав доступа недостаточно для выполнения операции;ProcessLookupError
— процесс, к которому обращается приложение не запущен или отсутствует;TimeoutError
— время ожидания истекло;
12 ReferenceError
— попытка доступа к объекту с помощью слабой ссылки, когда объект не существует;
13 RuntimeError
— генерируется в случае, когда исключение не может быть классифицировано или не подпадает под любую другую категорию;
14 NotImplementedError
— абстрактные методы класса нуждаются в переопределении;
15 SyntaxError
— ошибка синтаксиса;
16 SystemError
— сигнализирует о внутренне ошибке;
17 TypeError
— операция не может быть выполнена с переменной этого типа;
18 ValueError
— возникает когда в функцию передается объект правильного типа, но имеющий некорректное значение;
19 UnicodeError
— исключение связанное с кодирование текста в unicode
, бывает трех видов:
UnicodeEncodeError
— ошибка кодирования;UnicodeDecodeError
— ошибка декодирования;UnicodeTranslateError
— ошибка переводаunicode
.
20 Warning
— предупреждение, некритическая ошибка.
💭 Посмотреть всю цепочку наследования конкретного типа исключения можно с помощью модуля inspect
:
import inspect
print(inspect.getmro(TimeoutError))
> (<class 'TimeoutError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
📄 Подробное описание всех классов встроенных исключений в Python смотрите в официальной документации.
Как создать свой тип Exception
В Python можно создавать свои исключения. При этом есть одно обязательное условие: они должны быть потомками класса Exception
:
class MyError(Exception):
def __init__(self, text):
self.txt = text
try:
raise MyError('Моя ошибка')
except MyError as er:
print(er)
> Моя ошибка
С помощью try/except
контролируются и обрабатываются ошибки в приложении. Это особенно актуально для критически важных частей программы, где любые «падения» недопустимы (или могут привести к негативным последствиям). Например, если программа работает как «демон», падение приведет к полной остановке её работы. Или, например, при временном сбое соединения с базой данных, программа также прервёт своё выполнение (хотя можно было отловить ошибку и попробовать соединиться в БД заново).
Вместе с try/except
можно использовать дополнительные блоки. Если использовать все блоки описанные в статье, то код будет выглядеть так:
try:
# попробуем что-то сделать
except (ZeroDivisionError, ValueError) as e:
# обрабатываем исключения типа ZeroDivisionError или ValueError
except Exception as e:
# исключение не ZeroDivisionError и не ValueError
# поэтому обрабатываем исключение общего типа (унаследованное от Exception)
# сюда не сходят исключения типа GeneratorExit, KeyboardInterrupt, SystemExit
else:
# этот блок выполняется, если нет исключений
# если в этом блоке сделать return, он не будет вызван, пока не выполнился блок finally
finally:
# этот блок выполняется всегда, даже если нет исключений else будет проигнорирован
# если в этом блоке сделать return, то return в блоке
Подробнее о работе с исключениями в Python можно ознакомиться в официальной документации.